style_capsule 1.0.2

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.
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :style_capsule do
4
+ desc "Build StyleCapsule CSS files from components (similar to Tailwind CSS build)"
5
+ task build: :environment do
6
+ require "style_capsule/css_file_writer"
7
+
8
+ # Ensure output directory exists
9
+ StyleCapsule::CssFileWriter.ensure_output_directory
10
+
11
+ # Collect all component classes that use StyleCapsule
12
+ component_classes = []
13
+
14
+ # Find Phlex components
15
+ if defined?(Phlex::HTML)
16
+ ObjectSpace.each_object(Class) do |klass|
17
+ if klass < Phlex::HTML && klass.included_modules.include?(StyleCapsule::Component)
18
+ component_classes << klass
19
+ end
20
+ end
21
+ end
22
+
23
+ # Find ViewComponent components
24
+ # Wrap in begin/rescue to handle ViewComponent loading errors (e.g., version compatibility issues)
25
+ begin
26
+ if defined?(ViewComponent::Base)
27
+ ObjectSpace.each_object(Class) do |klass|
28
+ if klass < ViewComponent::Base && klass.included_modules.include?(StyleCapsule::ViewComponent)
29
+ component_classes << klass
30
+ end
31
+ rescue
32
+ # Skip this class if checking inheritance triggers ViewComponent loading errors
33
+ # (e.g., ViewComponent 2.83.0 has a bug with Gem::Version#to_f)
34
+ next
35
+ end
36
+ end
37
+ rescue
38
+ # ViewComponent may have loading issues (e.g., version compatibility)
39
+ # Silently skip ViewComponent components if there's an error
40
+ # This allows the rake task to continue with Phlex components
41
+ end
42
+
43
+ # Generate CSS files for each component
44
+ component_classes.each do |component_class|
45
+ next unless component_class.inline_cache_strategy == :file
46
+ # Check for class method component_styles (required for file caching)
47
+ next unless component_class.respond_to?(:component_styles, false)
48
+
49
+ begin
50
+ # Use class method component_styles for file caching
51
+ css_content = component_class.component_styles
52
+ next if css_content.nil? || css_content.to_s.strip.empty?
53
+
54
+ # Create a temporary instance to get capsule
55
+ # Some components might require arguments, so we catch errors
56
+ instance = component_class.new
57
+ capsule_id = instance.component_capsule
58
+ scoped_css = instance.send(:scope_css, css_content)
59
+
60
+ # Write CSS file
61
+ file_path = StyleCapsule::CssFileWriter.write_css(
62
+ css_content: scoped_css,
63
+ component_class: component_class,
64
+ capsule_id: capsule_id
65
+ )
66
+
67
+ puts "Generated: #{file_path}" if file_path
68
+ rescue ArgumentError, NoMethodError => e
69
+ # Component requires arguments or has dependencies - skip it
70
+ puts "Skipped #{component_class.name}: #{e.message}"
71
+ next
72
+ end
73
+ end
74
+
75
+ puts "StyleCapsule CSS files built successfully"
76
+ end
77
+
78
+ desc "Clear StyleCapsule generated CSS files"
79
+ task clear: :environment do
80
+ require "style_capsule/css_file_writer"
81
+ StyleCapsule::CssFileWriter.clear_files
82
+ puts "StyleCapsule CSS files cleared"
83
+ end
84
+ end
85
+
86
+ # Hook into Rails asset precompilation (similar to Tailwind CSS)
87
+ if defined?(Rails)
88
+ Rake::Task["assets:precompile"].enhance(["style_capsule:build"]) if Rake::Task.task_defined?("assets:precompile")
89
+ end
@@ -0,0 +1,110 @@
1
+ module StyleCapsule
2
+ VERSION: String
3
+
4
+ module CssProcessor
5
+ def self.scope_selectors: (String css_string, String capsule_id) -> String
6
+ def self.scope_with_nesting: (String css_string, String capsule_id) -> String
7
+ def self.strip_comments: (String css) -> String
8
+ end
9
+
10
+ class CssFileWriter
11
+ def self.configure: (?output_dir: String | Pathname, ?filename_pattern: Proc, ?enabled: bool) -> void
12
+ def self.write_css: (css_content: String, component_class: Class, capsule_id: String) -> String?
13
+ def self.file_exists?: (component_class: Class, capsule_id: String) -> bool
14
+ def self.file_path_for: (component_class: Class, capsule_id: String) -> String?
15
+ def self.ensure_output_directory: () -> void
16
+ def self.clear_files: () -> void
17
+ def self.enabled?: () -> bool
18
+ end
19
+
20
+ class StylesheetRegistry < ActiveSupport::CurrentAttributes
21
+ DEFAULT_NAMESPACE: Symbol
22
+
23
+ def self.normalize_namespace: (Symbol | String | nil namespace) -> Symbol
24
+ def self.register: (String file_path, ?namespace: Symbol | String | nil, **Hash[untyped, untyped] options) -> void
25
+ def self.register_inline: (String css_content, ?namespace: Symbol | String | nil, ?capsule_id: String | nil, ?cache_key: String | nil, ?cache_strategy: Symbol, ?cache_ttl: Integer | ActiveSupport::Duration | nil, ?cache_proc: Proc | nil, ?component_class: Class | nil, ?stylesheet_link_options: Hash[untyped, untyped] | nil) -> void
26
+ def self.cached_inline: (String cache_key, cache_strategy: Symbol, ?cache_ttl: Integer | ActiveSupport::Duration | nil, ?cache_proc: Proc | nil, ?css_content: String | nil, ?capsule_id: String | nil, ?namespace: Symbol | nil) -> String | nil
27
+ def self.cache_inline_css: (String cache_key, String css_content, cache_strategy: Symbol, ?cache_ttl: Integer | ActiveSupport::Duration | nil, ?cache_proc: Proc | nil, ?capsule_id: String | nil, ?namespace: Symbol | nil) -> void
28
+ def self.clear_inline_cache: (?String cache_key) -> void
29
+ def self.cleanup_expired_cache: () -> Integer
30
+ def self.manifest_files: () -> Hash[Symbol, Set[Hash[Symbol, untyped]]]
31
+ def self.request_inline_stylesheets: () -> Hash[Symbol, Array[Hash[Symbol, untyped]]]
32
+ def self.stylesheets_for: (?namespace: Symbol | String | nil) -> Array[Hash[Symbol, untyped]]
33
+ def self.clear: (?namespace: Symbol | String | nil) -> void
34
+ def self.clear_manifest: (?namespace: Symbol | String | nil) -> void
35
+ def self.render_head_stylesheets: (?ActionView::Base | nil view_context, ?namespace: Symbol | String | nil) -> String
36
+ def self.any?: (?namespace: Symbol | String | nil) -> bool
37
+ end
38
+
39
+ module Component
40
+ module ClassMethods
41
+ def css_cache: () -> Hash[String, String]
42
+ def clear_css_cache: () -> void
43
+ def capsule_id: () -> String | nil
44
+ def capsule_id: (String capsule_id) -> String
45
+ def stylesheet_registry: (?namespace: Symbol | String | nil, ?cache_strategy: Symbol, ?cache_ttl: Integer | ActiveSupport::Duration | nil, ?cache_proc: Proc | nil) -> void
46
+ def head_rendering!: () -> void # deprecated
47
+ def head_rendering?: () -> bool
48
+ def stylesheet_namespace: () -> Symbol | String | nil
49
+ def custom_capsule_id: () -> String | nil
50
+ def inline_cache_strategy: () -> Symbol | nil
51
+ def inline_cache_strategy: (Symbol strategy, ?ttl: Integer | nil, ?cache_proc: Proc | nil) -> Symbol
52
+ def inline_cache_ttl: () -> Integer | ActiveSupport::Duration | nil
53
+ def inline_cache_proc: () -> Proc | nil
54
+ def stylesheet_link_options: () -> Hash[untyped, untyped] | nil
55
+ def stylesheet_link_options: (Hash[untyped, untyped] options) -> Hash[untyped, untyped]
56
+ def css_scoping_strategy: () -> Symbol
57
+ def css_scoping_strategy: (Symbol strategy) -> Symbol
58
+ end
59
+
60
+ def component_capsule: () -> String
61
+ def head_rendering?: () -> bool
62
+ end
63
+
64
+ module ViewComponent
65
+ module ClassMethods
66
+ def css_cache: () -> Hash[String, String]
67
+ def clear_css_cache: () -> void
68
+ def capsule_id: () -> String | nil
69
+ def capsule_id: (String capsule_id) -> String
70
+ def stylesheet_registry: (?namespace: Symbol | String | nil, ?cache_strategy: Symbol, ?cache_ttl: Integer | ActiveSupport::Duration | nil, ?cache_proc: Proc | nil) -> void
71
+ def head_rendering!: () -> void # deprecated
72
+ def head_rendering?: () -> bool
73
+ def stylesheet_namespace: () -> Symbol | String | nil
74
+ def custom_capsule_id: () -> String | nil
75
+ def inline_cache_strategy: () -> Symbol | nil
76
+ def inline_cache_strategy: (Symbol strategy, ?ttl: Integer | nil, ?cache_proc: Proc | nil) -> Symbol
77
+ def inline_cache_ttl: () -> Integer | ActiveSupport::Duration | nil
78
+ def inline_cache_proc: () -> Proc | nil
79
+ def stylesheet_link_options: () -> Hash[untyped, untyped] | nil
80
+ def stylesheet_link_options: (Hash[untyped, untyped] options) -> Hash[untyped, untyped]
81
+ def css_scoping_strategy: () -> Symbol
82
+ def css_scoping_strategy: (Symbol strategy) -> Symbol
83
+ end
84
+
85
+ def component_capsule: () -> String
86
+ def head_rendering?: () -> bool
87
+ end
88
+
89
+ module ViewComponentHelper
90
+ def register_stylesheet: (String file_path, ?namespace: Symbol | String | nil, **Hash[untyped, untyped] options) -> void
91
+ def stylesheet_registrymap_tags: (?namespace: Symbol | String | nil) -> String
92
+ end
93
+
94
+ module Helper
95
+ def generate_capsule_id: (String css_content) -> String
96
+ def scope_css: (String css_content, String capsule_id) -> String
97
+ def style_capsule: (?String css_content, ?capsule_id: String | nil) { () -> String } -> String
98
+ def register_stylesheet: (String file_path, ?namespace: Symbol | String | nil, **Hash[untyped, untyped] options) -> void
99
+ def stylesheet_registrymap_tags: (?namespace: Symbol | String | nil) -> String
100
+ end
101
+
102
+ module PhlexHelper
103
+ def register_stylesheet: (String file_path, ?namespace: Symbol | String | nil, **Hash[untyped, untyped] options) -> void
104
+ def stylesheet_registrymap_tags: (?namespace: Symbol | String | nil) -> void
105
+ end
106
+
107
+ class Railtie < Rails::Railtie
108
+ end
109
+ end
110
+
metadata ADDED
@@ -0,0 +1,305 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: style_capsule
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Andrei Makarov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-11-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '9.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '6.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '9.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: railties
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '6.0'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '9.0'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '6.0'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '9.0'
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '3'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '3'
67
+ - !ruby/object:Gem::Dependency
68
+ name: webmock
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '3'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '3'
81
+ - !ruby/object:Gem::Dependency
82
+ name: rake
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '13'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '13'
95
+ - !ruby/object:Gem::Dependency
96
+ name: simplecov
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '0.22'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '0.22'
109
+ - !ruby/object:Gem::Dependency
110
+ name: rspec_junit_formatter
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '0.6'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '0.6'
123
+ - !ruby/object:Gem::Dependency
124
+ name: simplecov-cobertura
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '3'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '3'
137
+ - !ruby/object:Gem::Dependency
138
+ name: standard
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '1'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '1'
151
+ - !ruby/object:Gem::Dependency
152
+ name: standard-rails
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '1'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '1'
165
+ - !ruby/object:Gem::Dependency
166
+ name: standard-performance
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: '1'
172
+ type: :development
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - "~>"
177
+ - !ruby/object:Gem::Version
178
+ version: '1'
179
+ - !ruby/object:Gem::Dependency
180
+ name: appraisal
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - "~>"
184
+ - !ruby/object:Gem::Version
185
+ version: '2'
186
+ type: :development
187
+ prerelease: false
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - "~>"
191
+ - !ruby/object:Gem::Version
192
+ version: '2'
193
+ - !ruby/object:Gem::Dependency
194
+ name: memory_profiler
195
+ requirement: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - "~>"
198
+ - !ruby/object:Gem::Version
199
+ version: '1'
200
+ type: :development
201
+ prerelease: false
202
+ version_requirements: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - "~>"
205
+ - !ruby/object:Gem::Version
206
+ version: '1'
207
+ - !ruby/object:Gem::Dependency
208
+ name: rbs
209
+ requirement: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - "~>"
212
+ - !ruby/object:Gem::Version
213
+ version: '3'
214
+ type: :development
215
+ prerelease: false
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - "~>"
219
+ - !ruby/object:Gem::Version
220
+ version: '3'
221
+ - !ruby/object:Gem::Dependency
222
+ name: phlex-rails
223
+ requirement: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - "~>"
226
+ - !ruby/object:Gem::Version
227
+ version: '2.0'
228
+ type: :development
229
+ prerelease: false
230
+ version_requirements: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - "~>"
233
+ - !ruby/object:Gem::Version
234
+ version: '2.0'
235
+ - !ruby/object:Gem::Dependency
236
+ name: view_component
237
+ requirement: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - "~>"
240
+ - !ruby/object:Gem::Version
241
+ version: '4.0'
242
+ type: :development
243
+ prerelease: false
244
+ version_requirements: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - "~>"
247
+ - !ruby/object:Gem::Version
248
+ version: '4.0'
249
+ description: Provides component-scoped CSS encapsulation using [data-capsule] attributes
250
+ for Phlex components, ViewComponent components, and ERB templates. Styles are automatically
251
+ scoped to prevent leakage between components. Inspired by component-based CSS approaches
252
+ like Angular's view encapsulation and CSS modules.
253
+ email:
254
+ - contact@kiskolabs.com
255
+ executables: []
256
+ extensions: []
257
+ extra_rdoc_files: []
258
+ files:
259
+ - CHANGELOG.md
260
+ - LICENSE.md
261
+ - README.md
262
+ - SECURITY.md
263
+ - lib/style_capsule.rb
264
+ - lib/style_capsule/component.rb
265
+ - lib/style_capsule/component_styles_support.rb
266
+ - lib/style_capsule/css_file_writer.rb
267
+ - lib/style_capsule/css_processor.rb
268
+ - lib/style_capsule/helper.rb
269
+ - lib/style_capsule/phlex_helper.rb
270
+ - lib/style_capsule/railtie.rb
271
+ - lib/style_capsule/stylesheet_registry.rb
272
+ - lib/style_capsule/version.rb
273
+ - lib/style_capsule/view_component.rb
274
+ - lib/style_capsule/view_component_helper.rb
275
+ - lib/tasks/style_capsule.rake
276
+ - sig/style_capsule.rbs
277
+ homepage: https://github.com/amkisko/style_capsule.rb
278
+ licenses:
279
+ - MIT
280
+ metadata:
281
+ homepage_uri: https://github.com/amkisko/style_capsule.rb
282
+ changelog_uri: https://github.com/amkisko/style_capsule.rb/blob/main/CHANGELOG.md
283
+ bug_tracker_uri: https://github.com/amkisko/style_capsule.rb/issues
284
+ documentation_uri: https://github.com/amkisko/style_capsule.rb#readme
285
+ rubygems_mfa_required: 'true'
286
+ post_install_message:
287
+ rdoc_options: []
288
+ require_paths:
289
+ - lib
290
+ required_ruby_version: !ruby/object:Gem::Requirement
291
+ requirements:
292
+ - - ">="
293
+ - !ruby/object:Gem::Version
294
+ version: '3.0'
295
+ required_rubygems_version: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - ">="
298
+ - !ruby/object:Gem::Version
299
+ version: '0'
300
+ requirements: []
301
+ rubygems_version: 3.5.9
302
+ signing_key:
303
+ specification_version: 4
304
+ summary: Attribute-based CSS scoping for Phlex, ViewComponent, and ERB templates.
305
+ test_files: []