proscenium 0.9.1-aarch64-linux → 0.11.0.pre.1-aarch64-linux

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +423 -63
  3. data/lib/proscenium/builder.rb +126 -0
  4. data/lib/proscenium/css_module/path.rb +31 -0
  5. data/lib/proscenium/css_module/transformer.rb +76 -0
  6. data/lib/proscenium/css_module.rb +6 -28
  7. data/lib/proscenium/ensure_loaded.rb +27 -0
  8. data/lib/proscenium/ext/proscenium +0 -0
  9. data/lib/proscenium/ext/proscenium.h +19 -12
  10. data/lib/proscenium/helper.rb +62 -0
  11. data/lib/proscenium/importer.rb +110 -0
  12. data/lib/proscenium/libs/react-manager/index.jsx +88 -0
  13. data/lib/proscenium/libs/react-manager/react.js +2 -0
  14. data/lib/proscenium/libs/stimulus-loading.js +83 -0
  15. data/lib/proscenium/log_subscriber.rb +1 -2
  16. data/lib/proscenium/middleware/base.rb +1 -1
  17. data/lib/proscenium/middleware/esbuild.rb +3 -5
  18. data/lib/proscenium/middleware.rb +7 -1
  19. data/lib/proscenium/{side_load/monkey.rb → monkey.rb} +16 -12
  20. data/lib/proscenium/phlex/{resolve_css_modules.rb → css_modules.rb} +6 -20
  21. data/lib/proscenium/phlex/page.rb +2 -2
  22. data/lib/proscenium/phlex/react_component.rb +27 -64
  23. data/lib/proscenium/phlex.rb +10 -29
  24. data/lib/proscenium/railtie.rb +20 -22
  25. data/lib/proscenium/react_componentable.rb +94 -0
  26. data/lib/proscenium/resolver.rb +37 -0
  27. data/lib/proscenium/side_load.rb +13 -72
  28. data/lib/proscenium/source_path.rb +15 -0
  29. data/lib/proscenium/utils.rb +13 -0
  30. data/lib/proscenium/version.rb +1 -1
  31. data/lib/proscenium/view_component/css_modules.rb +11 -0
  32. data/lib/proscenium/view_component/react_component.rb +15 -28
  33. data/lib/proscenium/view_component/sideload.rb +4 -0
  34. data/lib/proscenium/view_component.rb +8 -31
  35. data/lib/proscenium.rb +24 -68
  36. metadata +21 -58
  37. data/lib/proscenium/css_module/class_names_resolver.rb +0 -66
  38. data/lib/proscenium/css_module/resolver.rb +0 -76
  39. data/lib/proscenium/current.rb +0 -9
  40. data/lib/proscenium/esbuild/golib.rb +0 -97
  41. data/lib/proscenium/esbuild.rb +0 -32
  42. data/lib/proscenium/phlex/component_concerns.rb +0 -27
  43. data/lib/proscenium/side_load/ensure_loaded.rb +0 -25
  44. data/lib/proscenium/side_load/helper.rb +0 -25
  45. data/lib/proscenium/view_component/tag_builder.rb +0 -23
@@ -2,83 +2,24 @@
2
2
 
3
3
  module Proscenium
4
4
  class SideLoad
5
- extend ActiveSupport::Autoload
6
-
7
- NotIncludedError = Class.new(StandardError)
8
-
9
- autoload :Monkey
10
- autoload :Helper
11
- autoload :EnsureLoaded
12
-
13
- EXTENSIONS = %i[js css].freeze
14
- EXTENSION_MAP = {
15
- '.css' => :css,
16
- # '.tsx' => :js,
17
- '.ts' => :js,
18
- # '.jsx' => :js,
19
- '.js' => :js
20
- }.freeze
21
-
22
- attr_reader :path
23
-
24
5
  class << self
25
- # Side load the given asset `path`, by appending it to `Proscenium::Current.loaded`, which is
26
- # a Set of 'js' and 'css' asset paths. This is idempotent, so side loading will never include
27
- # duplicates.
6
+ # Side loads the class, and its super classes that respond to `.source_path`.
28
7
  #
29
- # @return [Array] appended URL paths
30
- def append(path, extension_map = EXTENSION_MAP)
31
- new(path, extension_map).append
32
- end
33
-
34
- # Side load the given `path` at `type`, without first resolving the path. This still respects
35
- # idempotency of `Proscenium::Current.loaded`.
8
+ # Assign the `abstract_class` class variable to any abstract class, and it will not be side
9
+ # loaded. Additionally, if the class responds to `#sideload?`, and it returns false, it will
10
+ # not be side loaded.
36
11
  #
37
- # @param path [String]
38
- # @param type [Symbol] :js or :css
39
- def append!(path, type)
40
- return if Proscenium::Current.loaded[type].include?(path)
41
-
42
- Proscenium::Current.loaded[type] << log(path)
43
- end
44
-
45
- def log(value)
46
- ActiveSupport::Notifications.instrument('sideload.proscenium', identifier: value)
47
-
48
- value
49
- end
50
- end
51
-
52
- # @param path [Pathname, String] The path of the file to be side loaded.
53
- # @param extension_map [Hash] File extensions to side load.
54
- def initialize(path, extension_map = EXTENSION_MAP)
55
- @path = (path.is_a?(Pathname) ? path : Rails.root.join(path)).sub_ext('')
56
- @extension_map = extension_map
57
-
58
- Proscenium::Current.loaded ||= EXTENSIONS.index_with { |_e| Set.new }
59
- end
60
-
61
- def append
62
- @extension_map.filter_map do |ext, type|
63
- next unless (resolved_path = resolve_path(path.sub_ext(ext)))
64
-
65
- # Make sure path is not already side loaded.
66
- unless Proscenium::Current.loaded[type].include?(resolved_path)
67
- Proscenium::Current.loaded[type] << log(resolved_path)
12
+ # If the class responds to `.sideload`, it will be called instead of the regular side loading.
13
+ # You can use this to customise what is side loaded.
14
+ def sideload_inheritance_chain(obj)
15
+ return if !Proscenium.config.side_load || (obj.respond_to?(:sideload?) && !obj.sideload?)
16
+
17
+ klass = obj.class
18
+ while klass.respond_to?(:source_path) && klass.source_path && !klass.abstract_class
19
+ klass.respond_to?(:sideload) ? klass.sideload : Importer.sideload(klass.source_path)
20
+ klass = klass.superclass
68
21
  end
69
-
70
- resolved_path
71
22
  end
72
23
  end
73
-
74
- private
75
-
76
- def log(...)
77
- self.class.log(...)
78
- end
79
-
80
- def resolve_path(path)
81
- path.exist? ? Utils.resolve_path(path.to_s) : nil
82
- end
83
24
  end
84
25
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Include this into any class to expose a `source_path` class and instance method, which will return
4
+ # the absolute file system path to the current object.
5
+ module Proscenium::SourcePath
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def source_path
12
+ @source_path ||= name.nil? ? nil : Pathname.new(const_source_location(name).first)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module Utils
5
+ module_function
6
+
7
+ # @param value [#to_s] The value to create the digest from. This will usually be a `Pathname`.
8
+ # @return [String] digest of the given value.
9
+ def digest(value)
10
+ Digest::SHA1.hexdigest(value.to_s)[..7]
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- VERSION = '0.9.1'
4
+ VERSION = '0.11.0.pre.1'
5
5
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module ViewComponent::CssModules
5
+ include Proscenium::CssModule
6
+
7
+ def self.included(base)
8
+ base.extend CssModule::Path
9
+ end
10
+ end
11
+ end
@@ -1,35 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- #
4
- # Renders HTML markup suitable for use with @proscenium/component-manager.
5
- #
6
- # If a content block is given, that content will be rendered inside the component, allowing for a
7
- # "loading" UI. If no block is given, then a loading text will be rendered.
8
- #
9
- # The parent div is not decorated with any attributes, apart from the selector class required by
10
- # component-manager. But if your component has a side loaded CSS module stylesheet
11
- # (component.module.css), with a `.component` class defined, then that class will be assigned to the
12
- # parent div as a CSS module.
13
- #
14
- class Proscenium::ViewComponent::ReactComponent < Proscenium::ViewComponent
15
- self.abstract_class = true
3
+ module Proscenium
4
+ # Renders a <div> for use with React components, with data attributes specifying the component
5
+ # path and props.
6
+ #
7
+ # If a content block is given, that content will be rendered inside the component, allowing for a
8
+ # "loading" UI. If no block is given, then a "loading..." text will be rendered. It is intended
9
+ # that the component is mounted to this div, and the loading UI will then be replaced with the
10
+ # component's rendered output.
11
+ class ViewComponent::ReactComponent < ViewComponent
12
+ self.abstract_class = true
16
13
 
17
- attr_accessor :props, :lazy
14
+ include ReactComponentable
18
15
 
19
- # @param props: [Hash]
20
- # @param lazy: [Boolean] Lazy load the component using IntersectionObserver. Default: true.
21
- # @param [Block]
22
- def initialize(props: {}, lazy: true)
23
- @props = props
24
- @lazy = lazy
25
-
26
- super
27
- end
28
-
29
- def call
30
- tag.div class: ['componentManagedByProscenium', css_module(:component)],
31
- data: { component: { path: virtual_path, props: props, lazy: lazy } } do
32
- tag.div content || 'loading...'
16
+ def call
17
+ tag.send root_tag, data: data_attributes do
18
+ tag.div content || 'loading...'
19
+ end
33
20
  end
34
21
  end
35
22
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::ViewComponent::Sideload
4
+ end
@@ -4,52 +4,29 @@ require 'view_component'
4
4
 
5
5
  class Proscenium::ViewComponent < ViewComponent::Base
6
6
  extend ActiveSupport::Autoload
7
- include Proscenium::CssModule
8
7
 
9
- autoload :TagBuilder
8
+ autoload :Sideload
10
9
  autoload :ReactComponent
10
+ autoload :CssModules
11
+
12
+ include Proscenium::SourcePath
13
+ include CssModules
11
14
 
12
- # Side loads the class, and its super classes that respond to `.path`. Assign the `abstract_class`
13
- # class variable to any abstract class, and it will not be side loaded.
14
15
  module Sideload
15
16
  def before_render
16
- klass = self.class
17
- while !klass.abstract_class && klass.respond_to?(:path) && klass.path
18
- Proscenium::SideLoad.append klass.path
19
- klass = klass.superclass
20
- end
17
+ Proscenium::SideLoad.sideload_inheritance_chain self
21
18
 
22
19
  super
23
20
  end
24
21
  end
25
22
 
26
23
  class << self
27
- attr_accessor :path, :abstract_class
24
+ attr_accessor :abstract_class
28
25
 
29
26
  def inherited(child)
30
- child.path = if caller_locations(1, 1).first.label == 'inherited'
31
- Pathname.new caller_locations(2, 1).first.path
32
- else
33
- Pathname.new caller_locations(1, 1).first.path
34
- end
35
-
36
- child.prepend Sideload if Rails.application.config.proscenium.side_load
27
+ child.prepend Sideload
37
28
 
38
29
  super
39
30
  end
40
31
  end
41
-
42
- # @override Auto compilation of class names to css modules.
43
- def render_in(...)
44
- cssm.compile_class_names(super(...))
45
- end
46
-
47
- private
48
-
49
- # Overrides ActionView::Helpers::TagHelper::TagBuilder, allowing us to intercept the
50
- # `css_module` option from the HTML options argument of the `tag` and `content_tag` helpers, and
51
- # prepend it to the HTML `class` attribute.
52
- def tag_builder
53
- @tag_builder ||= Proscenium::ViewComponent::TagBuilder.new(self)
54
- end
55
32
  end
data/lib/proscenium.rb CHANGED
@@ -5,18 +5,36 @@ require 'active_support/dependencies/autoload'
5
5
  module Proscenium
6
6
  extend ActiveSupport::Autoload
7
7
 
8
- autoload :Current
8
+ FILE_EXTENSIONS = ['js', 'mjs', 'ts', 'jsx', 'tsx', 'css', 'js.map', 'mjs.map', 'jsx.map',
9
+ 'ts.map', 'tsx.map', 'css.map'].freeze
10
+
11
+ APPLICATION_INCLUDE_PATHS = ['config', 'app/assets', 'app/views', 'app/components', 'lib',
12
+ 'node_modules'].freeze
13
+
14
+ # Environment variables that should always be passed to the builder.
15
+ DEFAULT_ENV_VARS = Set['RAILS_ENV', 'NODE_ENV'].freeze
16
+
17
+ autoload :SourcePath
18
+ autoload :Utils
19
+ autoload :Monkey
9
20
  autoload :Middleware
21
+ autoload :EnsureLoaded
10
22
  autoload :SideLoad
11
23
  autoload :CssModule
24
+ autoload :ReactComponentable
12
25
  autoload :ViewComponent
13
26
  autoload :Phlex
14
27
  autoload :Helper
15
- autoload :Esbuild
16
-
17
- def self.reset_current_side_loaded
18
- Current.reset
19
- Current.loaded = SideLoad::EXTENSIONS.to_h { |e| [e, Set.new] }
28
+ autoload :Builder
29
+ autoload :Importer
30
+ autoload :Resolver
31
+
32
+ class Deprecator
33
+ def deprecation_warning(name, message, _caller_backtrace = nil)
34
+ msg = "`#{name}` is deprecated and will be removed in a near future release of Proscenium"
35
+ msg << " (#{message})" if message
36
+ Kernel.warn msg
37
+ end
20
38
  end
21
39
 
22
40
  class PathResolutionFailed < StandardError
@@ -29,68 +47,6 @@ module Proscenium
29
47
  "Path #{@path.inspect} cannot be resolved"
30
48
  end
31
49
  end
32
-
33
- module Utils
34
- module_function
35
-
36
- # @param value [#to_s] The value to create the digest from. This will usually be a `Pathname`.
37
- # @return [String] string digest of the given value.
38
- def digest(value)
39
- Digest::SHA1.hexdigest(value.to_s)[..7]
40
- end
41
-
42
- # Resolve the given `path` to a URL path.
43
- #
44
- # @param path [String] Can be URL path, file system path, or bare specifier (ie. NPM package).
45
- # @return [String] URL path.
46
- def resolve_path(path) # rubocop:disable Metrics/AbcSize
47
- raise ArgumentError, 'path must be a string' unless path.is_a?(String)
48
-
49
- if path.starts_with?('./', '../')
50
- raise ArgumentError, 'path must be an absolute file system or URL path'
51
- end
52
-
53
- matched_gem = Proscenium.config.side_load_gems.find do |_, opts|
54
- path.starts_with?("#{opts[:root]}/")
55
- end
56
-
57
- if matched_gem
58
- sroot = "#{matched_gem[1][:root]}/"
59
- relpath = path.delete_prefix(sroot)
60
-
61
- if (package_name = matched_gem[1][:package_name] || matched_gem[0])
62
- return Esbuild::Golib.resolve("#{package_name}/#{relpath}")
63
- end
64
-
65
- # TODO: manually resolve the path without esbuild
66
- raise PathResolutionFailed, path
67
- end
68
-
69
- return path.delete_prefix(Rails.root.to_s) if path.starts_with?("#{Rails.root}/")
70
-
71
- Esbuild::Golib.resolve(path)
72
- end
73
-
74
- # Resolves CSS class `names` to CSS module names. Each name will be converted to a CSS module
75
- # name, consisting of the camelCased name (lower case first character), and suffixed with the
76
- # given `digest`.
77
- #
78
- # @param names [String, Array]
79
- # @param digest: [String]
80
- # @returns [Array] of class names generated from the given CSS module `names` and `digest`.
81
- def css_modularise_class_names(*names, digest: nil)
82
- names.flatten.compact.map { |name| css_modularise_class_name name, digest: digest }
83
- end
84
-
85
- def css_modularise_class_name(name, digest: nil)
86
- sname = name.to_s
87
- if sname.starts_with?('_')
88
- "_#{sname[1..].camelize(:lower)}#{digest}"
89
- else
90
- "#{sname.camelize(:lower)}#{digest}"
91
- end
92
- end
93
- end
94
50
  end
95
51
 
96
52
  require 'proscenium/railtie'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proscenium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.11.0.pre.1
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Joel Moss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-11 00:00:00.000000000 Z
11
+ date: 2023-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,20 +44,6 @@ dependencies:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: 1.15.5
47
- - !ruby/object:Gem::Dependency
48
- name: nokogiri
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '1.13'
54
- type: :runtime
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '1.13'
61
47
  - !ruby/object:Gem::Dependency
62
48
  name: oj
63
49
  requirement: !ruby/object:Gem::Requirement
@@ -72,34 +58,6 @@ dependencies:
72
58
  - - "~>"
73
59
  - !ruby/object:Gem::Version
74
60
  version: '3.13'
75
- - !ruby/object:Gem::Dependency
76
- name: phlex
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: 1.8.1
82
- type: :runtime
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: 1.8.1
89
- - !ruby/object:Gem::Dependency
90
- name: phlex-rails
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: 1.0.0
96
- type: :runtime
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: 1.0.0
103
61
  - !ruby/object:Gem::Dependency
104
62
  name: railties
105
63
  requirement: !ruby/object:Gem::Requirement
@@ -131,34 +89,39 @@ files:
131
89
  - LICENSE.txt
132
90
  - README.md
133
91
  - lib/proscenium.rb
92
+ - lib/proscenium/builder.rb
134
93
  - lib/proscenium/css_module.rb
135
- - lib/proscenium/css_module/class_names_resolver.rb
136
- - lib/proscenium/css_module/resolver.rb
137
- - lib/proscenium/current.rb
138
- - lib/proscenium/esbuild.rb
139
- - lib/proscenium/esbuild/golib.rb
94
+ - lib/proscenium/css_module/path.rb
95
+ - lib/proscenium/css_module/transformer.rb
96
+ - lib/proscenium/ensure_loaded.rb
140
97
  - lib/proscenium/ext/proscenium
141
98
  - lib/proscenium/ext/proscenium.h
142
99
  - lib/proscenium/helper.rb
100
+ - lib/proscenium/importer.rb
101
+ - lib/proscenium/libs/react-manager/index.jsx
102
+ - lib/proscenium/libs/react-manager/react.js
103
+ - lib/proscenium/libs/stimulus-loading.js
143
104
  - lib/proscenium/log_subscriber.rb
144
105
  - lib/proscenium/middleware.rb
145
106
  - lib/proscenium/middleware/base.rb
146
107
  - lib/proscenium/middleware/esbuild.rb
147
108
  - lib/proscenium/middleware/url.rb
109
+ - lib/proscenium/monkey.rb
148
110
  - lib/proscenium/phlex.rb
149
- - lib/proscenium/phlex/component_concerns.rb
111
+ - lib/proscenium/phlex/css_modules.rb
150
112
  - lib/proscenium/phlex/page.rb
151
113
  - lib/proscenium/phlex/react_component.rb
152
- - lib/proscenium/phlex/resolve_css_modules.rb
153
114
  - lib/proscenium/railtie.rb
115
+ - lib/proscenium/react_componentable.rb
116
+ - lib/proscenium/resolver.rb
154
117
  - lib/proscenium/side_load.rb
155
- - lib/proscenium/side_load/ensure_loaded.rb
156
- - lib/proscenium/side_load/helper.rb
157
- - lib/proscenium/side_load/monkey.rb
118
+ - lib/proscenium/source_path.rb
119
+ - lib/proscenium/utils.rb
158
120
  - lib/proscenium/version.rb
159
121
  - lib/proscenium/view_component.rb
122
+ - lib/proscenium/view_component/css_modules.rb
160
123
  - lib/proscenium/view_component/react_component.rb
161
- - lib/proscenium/view_component/tag_builder.rb
124
+ - lib/proscenium/view_component/sideload.rb
162
125
  homepage: https://github.com/joelmoss/proscenium
163
126
  licenses:
164
127
  - MIT
@@ -178,11 +141,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
141
  version: 2.7.0
179
142
  required_rubygems_version: !ruby/object:Gem::Requirement
180
143
  requirements:
181
- - - ">="
144
+ - - ">"
182
145
  - !ruby/object:Gem::Version
183
- version: '0'
146
+ version: 1.3.1
184
147
  requirements: []
185
- rubygems_version: 3.4.13
148
+ rubygems_version: 3.4.17
186
149
  signing_key:
187
150
  specification_version: 4
188
151
  summary: The engine powering your Rails frontend
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Proscenium
4
- class CssModule::ClassNamesResolver
5
- def initialize(class_names, phlex_path)
6
- @class_names = class_names.split
7
- @stylesheets = {}
8
- @phlex_path = phlex_path.sub_ext('.module.css')
9
-
10
- resolve_class_names
11
- end
12
-
13
- def class_names
14
- @class_names.join(' ')
15
- end
16
-
17
- def stylesheets
18
- @stylesheets.map { |_, values| values[:resolved_path] }
19
- end
20
-
21
- private
22
-
23
- def resolve_class_names
24
- @class_names.map! do |class_name|
25
- if class_name.include?('/')
26
- if class_name.starts_with?('@')
27
- # Scoped bare specifier (eg. "@scoped/package/lib/button@default").
28
- _, path, name = class_name.split('@')
29
- path = "@#{path}"
30
- elsif class_name.starts_with?('/')
31
- # Local path with leading slash.
32
- path, name = class_name[1..].split('@')
33
- else
34
- # Bare specifier (eg. "mypackage/lib/button@default").
35
- path, name = class_name.split('@')
36
- end
37
-
38
- path += '.module.css'
39
-
40
- Utils.css_modularise_class_name name, digest: add_stylesheet(path)[:digest]
41
- elsif class_name.starts_with?('@')
42
- Utils.css_modularise_class_name class_name[1..],
43
- digest: add_stylesheet(@phlex_path)[:digest]
44
- else
45
- class_name
46
- end
47
- end
48
- end
49
-
50
- def add_stylesheet(path)
51
- return @stylesheets[path] if @stylesheets.key?(path)
52
-
53
- resolved_path = Utils.resolve_path(path.to_s)
54
-
55
- unless Rails.root.join(resolved_path[1..]).exist?
56
- raise CssModule::StylesheetNotFound, resolved_path
57
- end
58
-
59
- # Note that the digest is based on the resolved (URL) path, not the original path.
60
- @stylesheets[path] = {
61
- resolved_path: resolved_path,
62
- digest: Utils.digest(resolved_path)
63
- }
64
- end
65
- end
66
- end
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Proscenium
4
- class CssModule::Resolver
5
- attr_reader :side_loaded_paths
6
-
7
- # @param path [Pathname] Absolute file system path to the Ruby file that will be side loaded.
8
- def initialize(path, side_load: true, hash: nil)
9
- raise ArgumentError, "'#{path}' must be a `Pathname`" unless path.is_a?(Pathname)
10
-
11
- @path = path
12
- @hash = hash
13
- @css_module_path = path.sub_ext('.module.css')
14
- @side_load = side_load
15
- @side_loaded_paths = nil
16
- end
17
-
18
- # Parses the given `content` for CSS modules names ('class' attributes beginning with '@'), and
19
- # returns the content with said CSS Modules replaced with the compiled class names.
20
- #
21
- # Example:
22
- # <div class="@my_css_module_name"></div>
23
- def compile_class_names(content)
24
- doc = Nokogiri::HTML::DocumentFragment.parse(content)
25
-
26
- return content if (modules = doc.css('[class*="@"]')).empty?
27
-
28
- modules.each do |ele|
29
- classes = ele.classes.map { |cls| cls.starts_with?('@') ? class_names!(cls[1..]) : cls }
30
- ele['class'] = classes.join(' ')
31
- end
32
-
33
- doc.to_html.html_safe
34
- end
35
-
36
- # Resolves the given CSS class names to CSS modules. This will also side load the stylesheet if
37
- # it exists.
38
- #
39
- # @param names [String, Array]
40
- # @returns [Array] of class names generated from the given CSS module `names`.
41
- def class_names(*names)
42
- side_load_css_module
43
- Utils.css_modularise_class_names names, digest: @hash
44
- end
45
-
46
- # Like #class_names, but requires that the stylesheet exists.
47
- #
48
- # @param names [String, Array]
49
- # @raises Proscenium::CssModule::NotFound if stylesheet does not exists.
50
- # @see #class_names
51
- def class_names!(...)
52
- raise CssModule::StylesheetNotFound, @css_module_path unless @css_module_path.exist?
53
-
54
- class_names(...)
55
- end
56
-
57
- def side_loaded?
58
- @side_loaded_paths.present?
59
- end
60
-
61
- private
62
-
63
- def side_load_css_module
64
- return if !@side_load || !Rails.application.config.proscenium.side_load
65
-
66
- paths = SideLoad.append @path, { '.module.css' => :css }
67
-
68
- @side_loaded_paths = if paths.empty?
69
- nil
70
- else
71
- @hash = Utils.digest(paths[0])
72
- paths
73
- end
74
- end
75
- end
76
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/current_attributes'
4
-
5
- module Proscenium
6
- class Current < ActiveSupport::CurrentAttributes
7
- attribute :loaded
8
- end
9
- end