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.
data/README.md ADDED
@@ -0,0 +1,754 @@
1
+ # IslandJS Rails
2
+
3
+ [![CI](https://github.com/praxis-emergent/islandjs-rails/actions/workflows/github-actions-demo.yml/badge.svg)](https://github.com/praxis-emergent/islandjs-rails/actions/workflows/github-actions-demo.yml)
4
+ [![Test Coverage](https://img.shields.io/badge/coverage-89.09%25-brightgreen.svg)](coverage/index.html)
5
+ [![RSpec Tests](https://img.shields.io/badge/tests-162%20passing-brightgreen.svg)](spec/)
6
+ [![Rails 8 Ready](https://img.shields.io/badge/Rails%208-Ready-brightgreen.svg)](#rails-8-ready)
7
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.0-red.svg)](https://www.ruby-lang.org/)
8
+
9
+ IslandJS Rails supports the development of React (or other JS library) islands in Rails apps by synchronizing `package.json` defined dependencies with UMD libraries served in `public/islands/vendor`.
10
+
11
+ Write Turbo compatible JSX in `app/javascript/islands/components/` and render it with a `react_component` helper in ERB templates (including Turbo Stream partials) — Vue and other framework support can be added with a bit of work.
12
+
13
+ ## Quick Start
14
+
15
+ IslandJS Rails requires:
16
+ - **Node.js** 16+ with **npm** and **yarn** (development only)
17
+ - **Rails** 7+ (tested with Rails 8)
18
+
19
+ ### Installation
20
+
21
+ ```ruby
22
+ # Add to your Gemfile
23
+ gem 'islandjs-rails'
24
+ ```
25
+
26
+ ```bash
27
+ bundle install
28
+ rails islandjs:init
29
+ ```
30
+
31
+ ### Install React
32
+ ```bash
33
+ rails "islandjs:install[react,18.3.1]"
34
+ rails "islandjs:install[react-dom,18.3.1]"
35
+ ```
36
+
37
+ ### Render React Components
38
+ ```erb
39
+ <!-- In any view -->
40
+ <%= react_component('DashboardApp', { userId: current_user.id }) %>
41
+ ```
42
+
43
+ > 💡 **Turbo Cache Compatible**: React components automatically persist state across Turbo navigation! See [Turbo Cache Integration](#turbo-cache-integration) for details.
44
+
45
+ ### Write Modern JSX (with Turbo Cache Support)
46
+
47
+ Every React component should be written to accept a single `containerId` prop and rendered using the `react_component` view helper, which accepts a JSON object of props.
48
+
49
+ The props data passed into `react_component` is automatically available via `useTurboProps` and can be optionally cached using `useTurboCache` for persistence across Turbo navigation.
50
+
51
+ ```jsx
52
+ // jsx/components/DashboardApp.jsx
53
+ import React, { useState, useEffect } from 'react';
54
+ import { useTurboProps, useTurboCache } from '../utils/turbo.js';
55
+
56
+ function DashboardApp({ containerId }) {
57
+ // Read initial state from data-initial-state attribute
58
+ const initialProps = useTurboProps(containerId);
59
+
60
+ const [userId] = useState(initialProps.userId);
61
+ const [welcomeCount, setWelcomeCount] = useState(initialProps.welcomeCount || 0);
62
+
63
+ // Setup turbo cache persistence for state across navigation
64
+ useEffect(() => {
65
+ const cleanup = useTurboCache(containerId, { userId, welcomeCount }, true);
66
+ return cleanup;
67
+ }, [containerId, userId, welcomeCount]);
68
+
69
+ return (
70
+ <div>
71
+ <h2>Welcome user {userId}!</h2>
72
+ <p>You've visited this dashboard {welcomeCount} times</p>
73
+ <button onClick={() => setWelcomeCount(prev => prev + 1)}>
74
+ Visit Again
75
+ </button>
76
+ </div>
77
+ );
78
+ }
79
+
80
+ export default DashboardApp;
81
+ ```
82
+
83
+ ## Why IslandJS Rails?
84
+
85
+ ### Perfect for Rails 8
86
+ IslandJS Rails aligns perfectly with **Rails 8's philosophy** of simplicity and convention over configuration:
87
+
88
+ - **Asset Pipeline Simplification**: Rails 8 streamlined assets - IslandJS Rails fits seamlessly
89
+ - **Hotwire + React Islands**: The sweet spot for Rails 8 frontend development
90
+ - **Fast Development & Deployment**: Instant builds, no library rebundling
91
+
92
+ ### The Problem IslandJS Rails Solves
93
+ Modern Rails developers face a painful choice:
94
+ - **Bundle everything**: Massive webpack configs, slow builds, bundle bloat
95
+ - **Skip modern JS**: Miss out on React and popular npm packages
96
+
97
+ **Important Note:** IslandJS Rails works with packages that ship UMD builds. Many popular packages have UMD builds, but some modern packages do not — React 19+ removed UMD builds entirely. Future versions of IslandJS Rails will support local UMD generation for some packages (such as [React 19+](https://github.com/lofcz/umd-react)).
98
+
99
+ ### The IslandJS Rails Solution
100
+ ```bash
101
+ # Instead of complex webpack configuration:
102
+ rails "islandjs:install[react]"
103
+ rails "islandjs:install[lodash]"
104
+ ```
105
+
106
+ **Result**: Zero-to-no webpack configuration, instant prod builds, access to hundreds of UMD packages.
107
+
108
+ ## Rails 8 Ready
109
+
110
+ ✅ **Tested against Rails 8**
111
+ ✅ **Compatible with Rails 8 asset pipeline**
112
+ ✅ **Optimized for Hotwire/Turbo workflows**
113
+ ✅ **Zero-config React islands**
114
+
115
+ ## Core Features
116
+
117
+ - **Convention over Configuration** - Works with sensible defaults
118
+ - **Package.json Integration** - (npm + yarn)
119
+ - **CDN Downloads** - Fetches UMD builds from unpkg.com and jsdelivr.net
120
+ - **Rails Integration** - Serves auto-generated vendor UMD files for seamless integration
121
+ - **Webpack Externals** - Updates webpack config to prevent duplicate bundling while allowing development in jsx or other formats
122
+ - **Flexible Architecture** - Compose and namespace libraries as needed
123
+
124
+ ## CLI Commands
125
+
126
+ ### 📦 Package Management
127
+
128
+ #### Rails Tasks
129
+
130
+ ```bash
131
+ # Initialize IslandJS Rails in your project
132
+ rails islandjs:init
133
+
134
+ # Install packages (adds to package.json + saves to vendor directory)
135
+ rails "islandjs:install[react]"
136
+ rails "islandjs:install[react,18.3.1]" # With specific version
137
+ rails "islandjs:install[lodash]"
138
+
139
+ # Update packages (updates package.json + refreshes vendor files)
140
+ rails "islandjs:update[react]"
141
+ rails "islandjs:update[react,18.3.1]" # To specific version
142
+
143
+ # Remove packages (removes from package.json + deletes vendor files)
144
+ rails "islandjs:remove[react]"
145
+ rails "islandjs:remove[lodash]"
146
+
147
+ # Clean all UMD files (removes ALL vendor files)
148
+ rails islandjs:clean
149
+
150
+ # Show configuration
151
+ rails islandjs:config
152
+ ```
153
+
154
+ ### 🗂️ Vendor System Management
155
+
156
+ IslandJS Rails includes additional tasks for managing the vendor file system:
157
+
158
+ ```bash
159
+ # Rebuild the combined vendor bundle (when using :external_combined mode)
160
+ rails islandjs:vendor:rebuild
161
+
162
+ # Show vendor system status and file sizes
163
+ rails islandjs:vendor:status
164
+ ```
165
+
166
+ **Vendor System Modes:**
167
+ - **`:external_split`** (default): Each library served as separate file from `public/islands/vendor/`
168
+ - **`:external_combined`**: All libraries concatenated into single bundle with cache-busting hash
169
+
170
+ **Benefits of Vendor System:**
171
+ - 🚀 **Better Performance**: Browser caching, parallel downloads, no Base64 bloat
172
+ - 📦 **Scalable**: File size doesn't affect HTML parsing or memory usage
173
+ - 🔧 **Maintainable**: Clear separation between vendor libraries and application code
174
+ - 🌐 **CDN Ready**: Vendor files can be easily moved to CDN for global distribution (serving from CDN will be configurable granularly in future versions — where possible)
175
+
176
+ ### 🛠️ Development & Production Commands
177
+
178
+ For development and building your JavaScript:
179
+
180
+ ```bash
181
+ # Development - watch for changes and rebuild automatically
182
+ yarn watch
183
+ # Or with npm: npm run watch
184
+
185
+ # Production - build optimized bundle for deployment
186
+ yarn build
187
+ # Or with npm: npm run build
188
+
189
+ # Install dependencies (after adding packages via islandjs:install)
190
+ yarn install
191
+ # Or with npm: npm install
192
+ ```
193
+
194
+ **Development Workflow:**
195
+ 1. Run `yarn watch` (or `npm run watch`) in one terminal
196
+ 2. Edit your components in `app/javascript/islands/components/`
197
+ 3. Changes are automatically compiled to `public/islands_bundle.js`
198
+
199
+ **Production Deployment:**
200
+ 1. Run `yarn build` (or `npm run build`) to create optimized bundle
201
+ 2. Commit the built assets: `git add public/islands_* && git add public/islands/*`
202
+ 3. Deploy with confidence - assets are prebuilt
203
+
204
+ ## 📦 Working with Scoped Packages
205
+
206
+ ### What are Scoped Packages?
207
+
208
+ Scoped packages are npm packages that belong to a namespace, prefixed with `@`. Examples include:
209
+ - `@solana/web3.js`
210
+
211
+ ### Installation Syntax
212
+
213
+ When installing scoped packages, you **must** include the full package name with the `@` symbol:
214
+
215
+ ```bash
216
+ # ✅ Correct - Full scoped package name
217
+ rails "islandjs:install[@solana/web3.js,1.98.2]"
218
+
219
+ # ❌ Incorrect - Missing .js suffix
220
+ rails "islandjs:install[@solana/web3,1.98.2]"
221
+
222
+ # ❌ Incorrect - Missing scope
223
+ rails "islandjs:install[web3.js,1.98.2]"
224
+ ```
225
+
226
+ ### Shell Escaping
227
+
228
+ The `@` symbol is handled automatically by Rails task syntax when using double quotes. No additional escaping is needed:
229
+
230
+ ```bash
231
+ # ✅ Works perfectly
232
+ rails "islandjs:install[@solana/web3.js]"
233
+
234
+ # ✅ Also works (with version)
235
+ rails "islandjs:install[@solana/web3.js,1.98.2]"
236
+
237
+ # ⚠️ May not work in some shells without quotes
238
+ rails islandjs:install[@solana/web3.js] # Avoid this
239
+ ```
240
+
241
+ ### Global Name Detection
242
+
243
+ IslandJS Rails automatically converts scoped package names to valid JavaScript global names:
244
+
245
+ ```ruby
246
+ # Automatic conversions:
247
+ '@solana/web3.js' => 'solanaWeb3' # Scope removed, camelCase
248
+ ```
249
+
250
+ ### Custom Global Names
251
+
252
+ You can override the automatic global name detection for scoped packages:
253
+
254
+ Solana Web3.js is automatically detected with the built-in global name mapping `solanaWeb3`.
255
+
256
+ ### Usage in Components
257
+
258
+ Once installed, scoped packages work exactly like regular packages:
259
+
260
+ ```jsx
261
+ // jsx/components/SolanaComponent.jsx
262
+ import React from 'react';
263
+
264
+ function SolanaComponent() {
265
+ // solanaWeb3 is automatically available as a global variable on the window object
266
+ const connection = new window.solanaWeb3.Connection('https://api.devnet.solana.com');
267
+
268
+ return (
269
+ <div>
270
+ <h2>Solana Integration</h2>
271
+ <p>Connected to: {connection.rpcEndpoint}</p>
272
+ </div>
273
+ );
274
+ }
275
+
276
+ export default SolanaComponent;
277
+ ```
278
+
279
+ ### Webpack Externals
280
+
281
+ IslandJS Rails automatically configures webpack externals for scoped packages:
282
+
283
+ ```javascript
284
+ // webpack.config.js (auto-generated)
285
+ module.exports = {
286
+ externals: {
287
+ // IslandJS Rails managed externals - do not edit manually
288
+ "@solana/web3.js": "solanaWeb3",
289
+ "react": "React",
290
+ "react-dom": "ReactDOM"
291
+ },
292
+ // ... rest of config
293
+ };
294
+ ```
295
+
296
+ ### Troubleshooting Scoped Packages
297
+
298
+ **Issue: Package not found**
299
+ ```bash
300
+ # Check the exact package name on npm
301
+ npm view @solana/web3.js
302
+
303
+ # Ensure you're using the full name
304
+ rails "islandjs:install[@solana/web3.js]" # ✅ Correct
305
+ rails "islandjs:install[@solana/web3]" # ❌ Wrong
306
+ ```
307
+
308
+ **Issue: UMD not available**
309
+ ```bash
310
+ # Some scoped packages don't ship UMD builds
311
+ # Check package documentation or try alternatives
312
+ # Future IslandJS Rails versions will support local UMD generation
313
+ ```
314
+
315
+ ### ⚡ Quick Reference
316
+
317
+ | Command | What it does | Example |
318
+ |---------|--------------|---------|
319
+ | `install` | Adds package via yarn + downloads UMD + saves to vendor | `rails islandjs:install[react]` |
320
+ | `update` | Updates package version + refreshes UMD | `rails islandjs:update[react,18.3.1]` |
321
+ | `remove` | Removes package via yarn + deletes vendor files | `rails islandjs:remove[react]` |
322
+ | `clean` | Removes ALL vendor files (destructive!) | `rails islandjs:clean` |
323
+
324
+ ### Configuration
325
+
326
+ ```ruby
327
+ # config/initializers/islandjs.rb
328
+ IslandjsRails.configure do |config|
329
+ # Directory for ERB partials (default: app/views/shared/islands)
330
+ config.partials_dir = Rails.root.join('app/views/shared/islands')
331
+
332
+ # Webpack configuration path
333
+ config.webpack_config_path = Rails.root.join('webpack.config.js')
334
+
335
+ # Vendor file delivery mode (default: :external_split)
336
+ config.vendor_script_mode = :external_split # One file per library
337
+ # config.vendor_script_mode = :external_combined # Single combined bundle
338
+
339
+ # Vendor files directory (default: public/islands/vendor)
340
+ config.vendor_dir = Rails.root.join('public/islands/vendor')
341
+
342
+ # Combined bundle filename base (default: 'islands-vendor')
343
+ config.combined_basename = 'islands-vendor'
344
+
345
+ # Library loading order for combined bundles
346
+ config.vendor_order = ['react', 'react-dom', 'lodash']
347
+ end
348
+ ```
349
+
350
+ ## Rails Integration
351
+
352
+ ### Helpers
353
+
354
+ #### `islands`
355
+ Single helper that includes all UMD vendor scripts and your webpack bundle.
356
+
357
+ ```erb
358
+ <%= islands %>
359
+ ```
360
+
361
+ This automatically loads:
362
+ - All UMD libraries from vendor files (either split or combined mode)
363
+ - Your webpack bundle
364
+ - Debug information in development
365
+
366
+ #### `react_component(name, props, options)`
367
+ Renders a React component with Turbo-compatible lifecycle.
368
+
369
+ ```erb
370
+ <%= react_component('UserProfile', {
371
+ userId: current_user.id,
372
+ theme: 'dark'
373
+ }, {
374
+ container_id: 'profile-widget',
375
+ namespace: 'window.islandjsRails'
376
+ }) %>
377
+ ```
378
+
379
+ ## Turbo Cache Integration
380
+
381
+ IslandJS Rails includes **built-in Turbo cache compatibility** for React components, ensuring state persists seamlessly across navigation.
382
+
383
+ ### How It Works
384
+
385
+ The `react_component` helper automatically:
386
+ 1. **Stores initial props** as JSON in `data-initial-state` attributes
387
+ 2. **Generates unique container IDs** for each component instance
388
+ 3. **Passes only the container ID** to the React component
389
+
390
+ This allows React components to persist state changes back to the data attribute before turbo caches the page.
391
+
392
+ ### Example: Turbo-Compatible Component
393
+
394
+ See the complete working example: [`HelloWorld.jsx`](app/javascript/islands/components/HelloWorld.jsx)
395
+
396
+ ```jsx
397
+ import React, { useState, useEffect } from 'react';
398
+ import { useTurboProps, useTurboCache } from '../utils/turbo.js';
399
+
400
+ const HelloWorld = ({ containerId }) => {
401
+ // Read initial state from data-initial-state attribute
402
+ const initialProps = useTurboProps(containerId);
403
+
404
+ const [count, setCount] = useState(initialProps.count || 0);
405
+ const [message, setMessage] = useState(initialProps.message || "Hello!");
406
+
407
+ // ensures persists state across Turbo navigation
408
+ useEffect(() => {
409
+ const cleanup = useTurboCache(containerId, { count, message }, true);
410
+ return cleanup;
411
+ }, [containerId, count, message]);
412
+
413
+ return (
414
+ <div>
415
+ <p>{message}</p>
416
+ <button onClick={() => setCount(count + 1)}>
417
+ Clicked {count} times
418
+ </button>
419
+ </div>
420
+ );
421
+ };
422
+ ```
423
+
424
+ ### Usage in Views
425
+
426
+ ```erb
427
+ <!-- In any Rails view -->
428
+ <%= react_component('HelloWorld', {
429
+ message: 'Hello from Rails!',
430
+ count: 5
431
+ }) %>
432
+ ```
433
+
434
+ ### Live Demo
435
+
436
+ See the complete demo: [`react.html.erb`](app/views/islandjs_demo/react.html.erb)
437
+
438
+ The demo shows:
439
+ - ✅ **State persistence** across Turbo navigation
440
+ - ✅ **Automatic state restoration** when navigating back
441
+ - ✅ **Zero configuration** - works out of the box
442
+ - ✅ **Compatible with Turbo Drive** and all Hotwire features
443
+
444
+ ### Turbo Utility Functions
445
+
446
+ IslandJS Rails provides utility functions for Turbo compatibility:
447
+
448
+ ```javascript
449
+ // Get initial state from container's data attribute
450
+ const initialProps = useTurboProps(containerId);
451
+
452
+ // Set up automatic state persistence
453
+ const cleanup = useTurboCache(containerId, currentState, autoRestore);
454
+
455
+ // Manually persist state (if needed)
456
+ persistState(containerId, stateObject);
457
+ ```
458
+
459
+ ### Benefits
460
+
461
+ - **🔄 Seamless Navigation**: State survives Turbo page transitions
462
+ - **⚡ Zero Setup**: Works automatically with `react_component` helper
463
+ - **🎯 Rails-Native**: Designed specifically for Rails + Turbo workflows
464
+ - **🏝️ Island Architecture**: Each component manages its own state independently
465
+
466
+ ## Advanced Usage
467
+
468
+ ### Built-in Global Names
469
+
470
+ IslandJS Rails includes built-in global name mappings for popular libraries:
471
+
472
+ - `react` → `React`
473
+ - `react-dom` → `ReactDOM`
474
+ - `lodash` → `_`
475
+ - `@solana/web3.js` → `solanaWeb3`
476
+ - And more common libraries
477
+
478
+ For other packages, kebab-case names are automatically converted to camelCase.
479
+
480
+ ### Composable Architecture
481
+
482
+ ```javascript
483
+ // Create your own namespace (or use the default window.islandjsRails)
484
+ window.islandjsRails = {
485
+ React: window.React,
486
+ UI: window.MaterialUI,
487
+ Utils: window._,
488
+ Charts: window.Chart
489
+ };
490
+
491
+ // Use in components
492
+ const { React, UI, Utils } = window.islandjsRails;
493
+ ```
494
+
495
+ ### Webpack Integration
496
+
497
+ IslandJS Rails automatically updates your webpack externals:
498
+
499
+ ```javascript
500
+ // webpack.config.js (auto-generated)
501
+ module.exports = {
502
+ externals: {
503
+ 'react': 'React',
504
+ 'lodash': '_'
505
+ }
506
+ };
507
+ ```
508
+
509
+ ### Configuration Options
510
+
511
+ ```ruby
512
+ IslandjsRails.configure do |config|
513
+ # Directory for ERB partials (default: app/views/shared/islands)
514
+ config.partials_dir = Rails.root.join('app/views/shared/islands')
515
+
516
+ # Path to webpack config (default: webpack.config.js)
517
+ config.webpack_config_path = Rails.root.join('webpack.config.js')
518
+
519
+ # Path to package.json (default: package.json)
520
+ config.package_json_path = Rails.root.join('package.json')
521
+
522
+ # Vendor file delivery mode (default: :external_split)
523
+ config.vendor_script_mode = :external_split # One file per library
524
+ # config.vendor_script_mode = :external_combined # Single combined bundle
525
+
526
+ # Vendor files directory (default: public/islands/vendor)
527
+ config.vendor_dir = Rails.root.join('public/islands/vendor')
528
+
529
+ # Combined bundle filename base (default: 'islands-vendor')
530
+ config.combined_basename = 'islands-vendor'
531
+
532
+ # Library loading order for combined bundles
533
+ config.vendor_order = ['react', 'react-dom', 'lodash']
534
+
535
+ # Built-in global name mappings are automatically applied
536
+ # No custom configuration needed for common libraries
537
+ end
538
+ ```
539
+
540
+ ## Real-World Examples
541
+
542
+ ### React Dashboard Component
543
+
544
+ ```bash
545
+ # Install dependencies
546
+ rails islandjs:install[react]
547
+ rails islandjs:install[react-dom]
548
+ rails islandjs:install[chart.js]
549
+ ```
550
+
551
+ ```jsx
552
+ // jsx/components/Dashboard.jsx
553
+ import React, { useState, useEffect } from 'react';
554
+ import { useTurboProps, useTurboCache } from '../utils/turbo.js';
555
+
556
+ function Dashboard({ containerId }) {
557
+ const initialProps = useTurboProps(containerId);
558
+
559
+ const [data, setData] = useState(initialProps.data || []);
560
+ const [loading, setLoading] = useState(false);
561
+
562
+ // Setup turbo cache persistence
563
+ useEffect(() => {
564
+ const cleanup = useTurboCache(containerId, { data }, true);
565
+ return cleanup;
566
+ }, [containerId, data]);
567
+
568
+ useEffect(() => {
569
+ // Fetch dashboard data if not cached
570
+ if (data.length === 0) {
571
+ setLoading(true);
572
+ fetch('/api/dashboard')
573
+ .then(res => res.json())
574
+ .then(fetchedData => {
575
+ setData(fetchedData);
576
+ setLoading(false);
577
+ });
578
+ }
579
+ }, []);
580
+
581
+ if (loading) return <div>Loading dashboard...</div>;
582
+
583
+ return (
584
+ <div>
585
+ <h1>Dashboard</h1>
586
+ {/* Chart component here */}
587
+ <p>Data points: {data.length}</p>
588
+ </div>
589
+ );
590
+ }
591
+
592
+ export default Dashboard;
593
+ ```
594
+
595
+ ```erb
596
+ <!-- app/views/dashboard/show.html.erb -->
597
+ <%= islands %>
598
+ <%= react_component('Dashboard', { data: @dashboard_data }) %>
599
+ ```
600
+
601
+ ### Turbo + React Integration
602
+
603
+ **⭐ Recommended: Use Built-in Turbo Cache**
604
+
605
+ IslandJS Rails now includes automatic Turbo cache compatibility! See the [Turbo Cache Integration](#turbo-cache-integration) section above for the modern approach with zero manual setup.
606
+
607
+ **Alternative: Manual Turbo Integration**
608
+
609
+ For custom scenarios, you can manually handle Turbo events:
610
+
611
+ ```javascript
612
+ // Manual approach (not needed with react_component helper)
613
+ document.addEventListener('turbo:load', () => {
614
+ const container = document.getElementById('react-component');
615
+ if (container && !container.hasChildNodes()) {
616
+ ReactDOM.render(
617
+ React.createElement(MyComponent, {
618
+ data: JSON.parse(container.dataset.props)
619
+ }),
620
+ container
621
+ );
622
+ }
623
+ });
624
+
625
+ document.addEventListener('turbo:before-cache', () => {
626
+ // Cleanup React components before Turbo caches
627
+ document.querySelectorAll('[data-react-component]').forEach(el => {
628
+ ReactDOM.unmountComponentAtNode(el);
629
+ });
630
+ });
631
+ ```
632
+
633
+ ## Troubleshooting
634
+
635
+ ### Common Issues
636
+
637
+ **Package not found on CDN:**
638
+ ```ruby
639
+ # Some packages don't publish UMD builds
640
+ # Check unpkg.com/package-name/ for available files
641
+ # Consider using a different package or requesting UMD support
642
+ ```
643
+
644
+ **Global name conflicts:**
645
+ IslandJS Rails includes built-in mappings for common libraries. For packages with unusual global names, check the library's documentation or browser console to find the correct global variable name.
646
+
647
+ **Webpack externals not updating:**
648
+ ```bash
649
+ # Sync to update externals
650
+ rails islandjs:sync
651
+
652
+ # Or clean and reinstall
653
+ rails islandjs:clean
654
+ rails islandjs:install[react]
655
+ ```
656
+
657
+ ## Contributing
658
+
659
+ 1. Fork the repository
660
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
661
+ 3. Run the tests (`bundle exec rspec`)
662
+ 4. Commit your changes (`git commit -am 'Add amazing feature'`)
663
+ 5. Push to the branch (`git push origin feature/amazing-feature`)
664
+ 6. Open a Pull Request
665
+
666
+ ## License
667
+
668
+ MIT License - see LICENSE file for details.
669
+
670
+ ## Structure
671
+
672
+ ```
673
+ lib/islandjs_rails/
674
+ ├── spec/
675
+ │ ├── spec_helper.rb # Test setup and mocking
676
+ │ ├── lib/
677
+ │ │ ├── islandjs_rails_spec.rb # Main module tests
678
+ │ │ └── islandjs_rails/
679
+ │ │ ├── core_spec.rb # Core functionality tests
680
+ │ │ ├── rails_helpers_spec.rb # Rails helpers tests
681
+ │ │ ├── configuration_spec.rb # Configuration tests
682
+ │ │ ├── cli_spec.rb # CLI tests
683
+ │ │ ├── tasks_spec.rb # Rake tasks tests
684
+ │ │ ├── railtie_spec.rb # Rails integration tests
685
+ │ │ └── rails8_integration_spec.rb # Rails 8 specific tests
686
+ │ ├── fixtures/ # Test fixtures
687
+ │ └── support/ # Test support files
688
+ ├── coverage/ # SimpleCov coverage reports
689
+ ├── Gemfile # Test dependencies
690
+ ├── Rakefile # Test runner configuration
691
+ └── README.md # This file
692
+ ```
693
+
694
+ ## Running Tests
695
+
696
+ ### From the gem directory:
697
+
698
+ ```bash
699
+ cd lib/islandjs_rails
700
+ bundle install
701
+ bundle exec rspec
702
+ ```
703
+
704
+ ### Coverage Reports:
705
+
706
+ ```bash
707
+ # View coverage in terminal
708
+ bundle exec rspec
709
+
710
+ # Open coverage report in browser
711
+ open coverage/index.html
712
+ ```
713
+
714
+ ## Adding New Tests
715
+
716
+ When adding new IslandJS Rails functionality:
717
+
718
+ 1. Add tests to the appropriate test file
719
+ 2. Use the provided test helpers for consistency
720
+ 3. Mock external dependencies (CDN calls, file system operations)
721
+ 4. Test both success and failure scenarios
722
+ 5. Ensure tests are isolated and don't affect each other
723
+
724
+ ## Future Enhancements
725
+
726
+ Planned features for future releases:
727
+
728
+ - **Server-Side Rendering (SSR)**: Pre-render React components on the server
729
+ - **Component Caching**: Intelligent caching of rendered components
730
+ - **Hot Reloading**: Development mode hot reloading for React components
731
+ - **TypeScript Support**: First-class TypeScript support for UMD packages
732
+ - **Local UMD Generation**: Generate UMD builds for packages that don't ship them
733
+ - **Multi-framework Support**: Vue, Svelte, and other frameworks
734
+ - **Build-time Optimization**: Optional build-time bundling for production
735
+ - **Edge Computing**: Cloudflare Workers and similar platform support
736
+
737
+ ---
738
+
739
+ ## Rails 8 Integration Benefits
740
+
741
+ ### 🚀 **Perfect for Rails 8 Philosophy**
742
+ - **Convention over Configuration**: Install React in one command
743
+ - **The Rails Way**: Simple, opinionated, productive
744
+ - **Modern Without Complexity**: React islands, not SPAs
745
+
746
+ ### ⚡ **Performance Optimized**
747
+ - **Instant Builds**: No bundling external libraries
748
+ - **Small Bundles**: Only your app code gets bundled
749
+ - **Fast Deploys**: CDN libraries cache globally
750
+
751
+ ### 🎯 **Developer Experience**
752
+ - **Zero Webpack Expertise**: Rails developers stay in Rails
753
+ - **Turbo Compatible**: Seamless navigation and caching
754
+ - **Progressive Enhancement**: Start with Hotwire, add React islands