islandjs-rails 0.3.0 → 0.5.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +1 -0
- data/lib/islandjs_rails/rails_helpers.rb +79 -20
- data/lib/islandjs_rails/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80cbddb05a05365fd47474178fd2fb2b403f69c597169fe16bcb93e558dbabb7
|
4
|
+
data.tar.gz: f342dd7687658b6c8df47cfa7566f2061517112eb248719c997b20ea2634d5bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e3ed8b8c57007f2011c9b875cef7a84b4b4f262038bc2f9c92cc7e98ebfd9ed66b73c2b0fd7e9a1da9bee499b2a036f0c9ed48dd34dd9fdc99e680d82dae908
|
7
|
+
data.tar.gz: ec2afebea06a97b95b7c22b71596c30b704e44bbf5c5a8b0579a497b5b44f89056efcdc5dbf2179564d74b921301740e4feef340d8257c3adbd7c72203c9b703
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.5.0] - 2025-09-29
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- **CSP support for script tags**: All IslandJS-generated `<script>` tags now automatically include a CSP nonce when one is present in the Rails request.
|
12
|
+
- **Flexible script attributes**: Helpers (`react_component`, `vue_component`, etc.) now support passing standard script attributes (`nonce`, `defer`, `async`, `crossorigin`, `integrity`).
|
13
|
+
|
14
|
+
## [0.4.0] - 2025-08-10
|
15
|
+
|
16
|
+
### Added
|
17
|
+
- Add ENV flag to control the dev UMD bundle info footer. The floating footer is disabled by default and only shows in development when `ISLANDJS_RAILS_SHOW_UMD_DEBUG` is truthy.
|
18
|
+
|
8
19
|
## [0.3.0] - 2025-08-09
|
9
20
|
|
10
21
|
### Added
|
data/README.md
CHANGED
@@ -420,6 +420,7 @@ Renders a React component with Turbo-compatible lifecycle and optional placehold
|
|
420
420
|
- `class`: CSS class for container
|
421
421
|
- `placeholder_class`: CSS class for placeholder content
|
422
422
|
- `placeholder_style`: Inline styles for placeholder content
|
423
|
+
- Script attributes: `nonce` (auto-detected for CSP), `defer`, `async`, `crossorigin`, `integrity`
|
423
424
|
|
424
425
|
## Placeholder Support
|
425
426
|
|
@@ -1,11 +1,14 @@
|
|
1
1
|
module IslandjsRails
|
2
2
|
module RailsHelpers
|
3
|
+
# Script attributes that can be passed through options
|
4
|
+
SCRIPT_ATTRIBUTES = %i[nonce defer async crossorigin integrity].freeze
|
5
|
+
|
3
6
|
# Main helper method that combines all IslandJS functionality
|
4
|
-
def islands
|
7
|
+
def islands(**attributes)
|
5
8
|
output = []
|
6
9
|
output << island_partials # Now uses vendor UMD partial
|
7
|
-
output << island_bundle_script
|
8
|
-
output << umd_versions_debug if
|
10
|
+
output << island_bundle_script(**attributes)
|
11
|
+
output << umd_versions_debug if umd_debug_enabled?
|
9
12
|
output.compact.join("\n").html_safe
|
10
13
|
end
|
11
14
|
|
@@ -22,29 +25,32 @@ module IslandjsRails
|
|
22
25
|
end
|
23
26
|
|
24
27
|
# Render the main IslandJS bundle script tag
|
25
|
-
def island_bundle_script
|
28
|
+
def island_bundle_script(**attributes)
|
26
29
|
manifest_path = Rails.root.join('public', 'islands_manifest.json')
|
27
30
|
bundle_path = '/islands_bundle.js'
|
28
|
-
|
31
|
+
|
32
|
+
# Get formatted HTML attributes with defaults (including auto-nonce and defer)
|
33
|
+
html_attributes = script_html_attributes(defer: true, **attributes)
|
34
|
+
|
29
35
|
unless File.exist?(manifest_path)
|
30
36
|
# Fallback to direct bundle path when no manifest
|
31
|
-
return html_safe_string("<script src=\"#{bundle_path}\"
|
37
|
+
return html_safe_string("<script src=\"#{bundle_path}\"#{html_attributes}></script>")
|
32
38
|
end
|
33
|
-
|
39
|
+
|
34
40
|
begin
|
35
41
|
manifest = JSON.parse(File.read(manifest_path))
|
36
42
|
# Look for islands_bundle.js in manifest
|
37
43
|
bundle_file = manifest['islands_bundle.js']
|
38
|
-
|
44
|
+
|
39
45
|
if bundle_file
|
40
|
-
html_safe_string("<script src=\"#{bundle_file}\"
|
46
|
+
html_safe_string("<script src=\"#{bundle_file}\"#{html_attributes}></script>")
|
41
47
|
else
|
42
48
|
# Fallback to direct bundle path
|
43
|
-
html_safe_string("<script src=\"#{bundle_path}\"
|
49
|
+
html_safe_string("<script src=\"#{bundle_path}\"#{html_attributes}></script>")
|
44
50
|
end
|
45
51
|
rescue JSON::ParserError
|
46
52
|
# Fallback to direct bundle path on manifest parse error
|
47
|
-
html_safe_string("<script src=\"#{bundle_path}\"
|
53
|
+
html_safe_string("<script src=\"#{bundle_path}\"#{html_attributes}></script>")
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
@@ -66,7 +72,11 @@ module IslandjsRails
|
|
66
72
|
# Handle placeholder options
|
67
73
|
placeholder_class = options[:placeholder_class]
|
68
74
|
placeholder_style = options[:placeholder_style]
|
69
|
-
|
75
|
+
|
76
|
+
# Extract script attributes from options
|
77
|
+
script_attributes = options.slice(*SCRIPT_ATTRIBUTES)
|
78
|
+
script_attributes.compact!
|
79
|
+
|
70
80
|
# For turbo-cache compatibility, store initial state as JSON in data attribute
|
71
81
|
initial_state_json = props.to_json
|
72
82
|
|
@@ -91,7 +101,7 @@ module IslandjsRails
|
|
91
101
|
end
|
92
102
|
|
93
103
|
# Generate the mounting script - pass container_id as the only prop for turbo-cache pattern
|
94
|
-
mount_script = generate_react_mount_script(component_name, component_id, namespace, namespace_with_optional)
|
104
|
+
mount_script = generate_react_mount_script(component_name, component_id, namespace, namespace_with_optional, **script_attributes)
|
95
105
|
|
96
106
|
# Return the container div with data-initial-state and script
|
97
107
|
data_part = data_attrs.empty? ? '' : " #{data_attrs}"
|
@@ -133,9 +143,13 @@ module IslandjsRails
|
|
133
143
|
# Extract options
|
134
144
|
tag_name = options[:tag] || 'div'
|
135
145
|
css_class = options[:class] || ''
|
136
|
-
|
146
|
+
|
147
|
+
# Extract script attributes from options
|
148
|
+
script_attributes = options.slice(*SCRIPT_ATTRIBUTES)
|
149
|
+
script_attributes.compact!
|
150
|
+
|
137
151
|
# Generate the mounting script
|
138
|
-
mount_script = generate_vue_mount_script(component_name, component_id, props_json)
|
152
|
+
mount_script = generate_vue_mount_script(component_name, component_id, props_json, **script_attributes)
|
139
153
|
|
140
154
|
# Return the container div and script
|
141
155
|
container_html = "<#{tag_name} id=\"#{component_id}\" class=\"#{css_class}\"></#{tag_name}>"
|
@@ -181,7 +195,7 @@ module IslandjsRails
|
|
181
195
|
|
182
196
|
# Legacy UMD helper methods for backward compatibility with tests
|
183
197
|
def umd_versions_debug
|
184
|
-
return unless
|
198
|
+
return unless umd_debug_enabled?
|
185
199
|
|
186
200
|
begin
|
187
201
|
installed = IslandjsRails.core.send(:installed_packages)
|
@@ -237,6 +251,47 @@ module IslandjsRails
|
|
237
251
|
|
238
252
|
private
|
239
253
|
|
254
|
+
# Format HTML attributes into a string
|
255
|
+
# Returns a string like ' nonce="abc123" defer' or empty string if no attributes
|
256
|
+
def format_html_attributes(**attributes)
|
257
|
+
return '' if attributes.empty?
|
258
|
+
|
259
|
+
attributes.filter_map do |key, value|
|
260
|
+
next if value.nil? || value == false
|
261
|
+
key_str = key.to_s.tr('_', '-')
|
262
|
+
value == true ? " #{key_str}" : " #{key_str}=\"#{value}\""
|
263
|
+
end.join
|
264
|
+
end
|
265
|
+
|
266
|
+
# Get default script attributes with auto-nonce detection
|
267
|
+
def default_script_attributes(**user_attributes)
|
268
|
+
attributes = {}
|
269
|
+
|
270
|
+
# Auto-add nonce if CSP is enabled and not explicitly provided
|
271
|
+
if !user_attributes.key?(:nonce) && respond_to?(:content_security_policy_nonce)
|
272
|
+
nonce = content_security_policy_nonce
|
273
|
+
attributes[:nonce] = nonce if nonce
|
274
|
+
end
|
275
|
+
|
276
|
+
attributes.merge(user_attributes)
|
277
|
+
end
|
278
|
+
|
279
|
+
# Get formatted HTML attributes string for script tags
|
280
|
+
def script_html_attributes(**attributes)
|
281
|
+
script_attributes = default_script_attributes(**attributes)
|
282
|
+
format_html_attributes(**script_attributes)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Whether the floating UMD versions debug footer should render
|
286
|
+
def umd_debug_enabled?
|
287
|
+
return false unless Rails.env.development?
|
288
|
+
|
289
|
+
env_value = ENV['ISLANDJS_RAILS_SHOW_UMD_DEBUG']
|
290
|
+
return false if env_value.nil?
|
291
|
+
|
292
|
+
%w[1 true yes on].include?(env_value.to_s.strip.downcase)
|
293
|
+
end
|
294
|
+
|
240
295
|
# Find the bundle file path (with manifest support)
|
241
296
|
def find_bundle_path
|
242
297
|
# Try manifest first (production)
|
@@ -264,9 +319,11 @@ module IslandjsRails
|
|
264
319
|
end
|
265
320
|
|
266
321
|
# Generate React component mounting script with Turbo compatibility
|
267
|
-
def generate_react_mount_script(component_name, component_id, namespace, namespace_with_optional)
|
322
|
+
def generate_react_mount_script(component_name, component_id, namespace, namespace_with_optional, **attributes)
|
323
|
+
html_attributes = script_html_attributes(**attributes)
|
324
|
+
|
268
325
|
<<~JAVASCRIPT
|
269
|
-
<script>
|
326
|
+
<script#{html_attributes}>
|
270
327
|
(function() {
|
271
328
|
function mount#{component_name}() {
|
272
329
|
const container = document.getElementById('#{component_id}');
|
@@ -350,9 +407,11 @@ module IslandjsRails
|
|
350
407
|
end
|
351
408
|
|
352
409
|
# Generate Vue component mounting script with Turbo compatibility
|
353
|
-
def generate_vue_mount_script(component_name, component_id, props_json)
|
410
|
+
def generate_vue_mount_script(component_name, component_id, props_json, **attributes)
|
411
|
+
html_attributes = script_html_attributes(**attributes)
|
412
|
+
|
354
413
|
<<~JAVASCRIPT
|
355
|
-
<script>
|
414
|
+
<script#{html_attributes}>
|
356
415
|
(function() {
|
357
416
|
let vueApp = null;
|
358
417
|
|