proscenium 0.10.0-x86_64-linux → 0.11.0.pre.2-x86_64-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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +175 -39
  3. data/lib/proscenium/builder.rb +17 -11
  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 +2 -1
  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/test.js +1 -0
  15. data/lib/proscenium/middleware/base.rb +2 -2
  16. data/lib/proscenium/middleware/esbuild.rb +2 -4
  17. data/lib/proscenium/middleware/runtime.rb +18 -0
  18. data/lib/proscenium/middleware.rb +4 -1
  19. data/lib/proscenium/{side_load/monkey.rb → monkey.rb} +11 -15
  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 +26 -27
  23. data/lib/proscenium/phlex.rb +10 -29
  24. data/lib/proscenium/railtie.rb +14 -26
  25. data/lib/proscenium/react_componentable.rb +94 -0
  26. data/lib/proscenium/resolver.rb +41 -0
  27. data/lib/proscenium/side_load.rb +13 -73
  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 -15
  33. data/lib/proscenium/view_component/sideload.rb +4 -0
  34. data/lib/proscenium/view_component.rb +8 -38
  35. data/lib/proscenium.rb +23 -68
  36. metadata +21 -29
  37. data/lib/proscenium/componentable.rb +0 -63
  38. data/lib/proscenium/css_module/class_names_resolver.rb +0 -66
  39. data/lib/proscenium/css_module/resolver.rb +0 -76
  40. data/lib/proscenium/current.rb +0 -9
  41. data/lib/proscenium/phlex/component_concerns.rb +0 -9
  42. data/lib/proscenium/side_load/ensure_loaded.rb +0 -25
  43. data/lib/proscenium/side_load/helper.rb +0 -41
  44. data/lib/proscenium/view_component/tag_builder.rb +0 -23
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module ReactComponentable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ # @return [Hash] the props to pass to the React component.
9
+ attr_writer :props
10
+
11
+ # The HTML tag to use as the wrapping element for the component. You can reassign this in your
12
+ # component class to use a different tag:
13
+ #
14
+ # class MyComponent < Proscenium::ViewComponent::ReactComponent
15
+ # self.root_tag = :span
16
+ # end
17
+ #
18
+ # @return [Symbol]
19
+ class_attribute :root_tag, instance_predicate: false, default: :div
20
+
21
+ # By default, the template block (content) of the component will be server rendered as normal.
22
+ # However, when React hydrates and takes control of the component, it's content will be
23
+ # replaced by React with the JavaScript rendered content. Enabling this option will forward
24
+ # the server rendered content as the `children` prop passed to the React component.
25
+ #
26
+ # @example
27
+ #
28
+ # const Component = ({ children }) => {
29
+ # return <div dangerouslySetInnerHTML={{ __html: children }} />
30
+ # }
31
+ #
32
+ # @return [Boolean]
33
+ class_attribute :forward_children, default: false
34
+
35
+ # Lazy load the component using IntersectionObserver?
36
+ #
37
+ # @return [Boolean]
38
+ class_attribute :lazy, default: false
39
+
40
+ class_attribute :loader
41
+ end
42
+
43
+ class_methods do
44
+ # Import only the component manager. The component itself is side loaded in the initializer,
45
+ # so that it can be lazy loaded based on the value of the `lazy` instance variable.
46
+ def sideload
47
+ Importer.import resolve: '@proscenium/react-manager/index.jsx'
48
+ Importer.sideload source_path, lazy: true
49
+ end
50
+ end
51
+
52
+ # @param props: [Hash]
53
+ def initialize(lazy: self.class.lazy, loader: self.class.loader, props: {})
54
+ self.lazy = lazy
55
+ self.loader = loader
56
+ @props = props
57
+ end
58
+
59
+ # The absolute URL path to the javascript component.
60
+ def virtual_path
61
+ @virtual_path ||= Resolver.resolve self.class.source_path.sub_ext('.jsx').to_s
62
+ end
63
+
64
+ def props
65
+ @props ||= {}
66
+ end
67
+
68
+ private
69
+
70
+ def data_attributes
71
+ {
72
+ proscenium_component_path: Pathname.new(virtual_path).to_s,
73
+ proscenium_component_props: prepared_props,
74
+ proscenium_component_lazy: lazy
75
+ }.tap do |x|
76
+ x[:proscenium_component_forward_children] = true if forward_children?
77
+ end
78
+ end
79
+
80
+ def prepared_props
81
+ props.deep_transform_keys do |term|
82
+ # This ensures that the first letter after a slash is not capitalized.
83
+ string = term.to_s.split('/').map { |str| str.camelize :lower }.join('/')
84
+
85
+ # Reverses the effect of ActiveSupport::Inflector.camelize converting slashes into `::`.
86
+ string.gsub '::', '/'
87
+ end.to_json
88
+ end
89
+
90
+ def loader_component
91
+ render Loader::Component.new(loader, @html_class, data_attributes, tag: @html_tag)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/current_attributes'
4
+
5
+ module Proscenium
6
+ class Resolver < ActiveSupport::CurrentAttributes
7
+ # TODO: cache this across requests in production.
8
+ attribute :resolved
9
+
10
+ # Resolve the given `path` to a URL path.
11
+ #
12
+ # @param path [String] Can be URL path, file system path, or bare specifier (ie. NPM package).
13
+ # @return [String] URL path.
14
+ def self.resolve(path) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
15
+ self.resolved ||= {}
16
+
17
+ self.resolved[path] ||= begin
18
+ if path.start_with?('./', '../')
19
+ raise ArgumentError, 'path must be an absolute file system or URL path'
20
+ end
21
+
22
+ if path.start_with?('@proscenium/')
23
+ "/#{path}"
24
+ elsif (gem = Proscenium.config.side_load_gems.find do |_, x|
25
+ path.start_with? "#{x[:root]}/"
26
+ end)
27
+ unless (package_name = gem[1][:package_name] || gem[0])
28
+ # TODO: manually resolve the path without esbuild
29
+ raise PathResolutionFailed, path
30
+ end
31
+
32
+ Builder.resolve "#{package_name}/#{path.delete_prefix("#{gem[1][:root]}/")}"
33
+ elsif path.start_with?("#{Rails.root}/")
34
+ path.delete_prefix Rails.root.to_s
35
+ else
36
+ Builder.resolve path
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -2,84 +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
- '.module.css' => :css,
16
- '.css' => :css,
17
- '.tsx' => :js,
18
- '.ts' => :js,
19
- '.jsx' => :js,
20
- '.js' => :js
21
- }.freeze
22
-
23
- attr_reader :path
24
-
25
5
  class << self
26
- # Side load the given asset `path`, by appending it to `Proscenium::Current.loaded`, which is
27
- # a Set of 'js' and 'css' asset paths. This is idempotent, so side loading will never include
28
- # duplicates.
6
+ # Side loads the class, and its super classes that respond to `.source_path`.
29
7
  #
30
- # @return [Array] appended URL paths
31
- def append(path, extension_map = EXTENSION_MAP)
32
- new(path, extension_map).append
33
- end
34
-
35
- # Side load the given `path` at `type`, without first resolving the path. This still respects
36
- # 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.
37
11
  #
38
- # @param path [String]
39
- # @param type [Symbol] :js or :css
40
- def append!(path, type)
41
- return if Proscenium::Current.loaded[type].include?(path)
42
-
43
- Proscenium::Current.loaded[type] << log(path)
44
- end
45
-
46
- def log(value)
47
- ActiveSupport::Notifications.instrument('sideload.proscenium', identifier: value)
48
-
49
- value
50
- end
51
- end
52
-
53
- # @param path [Pathname, String] The path of the file to be side loaded.
54
- # @param extension_map [Hash] File extensions to side load.
55
- def initialize(path, extension_map = EXTENSION_MAP)
56
- @path = (path.is_a?(Pathname) ? path : Rails.root.join(path)).sub_ext('')
57
- @extension_map = extension_map
58
-
59
- Proscenium::Current.loaded ||= EXTENSIONS.index_with { |_e| Set.new }
60
- end
61
-
62
- def append
63
- @extension_map.filter_map do |ext, type|
64
- next unless (resolved_path = resolve_path(path.sub_ext(ext)))
65
-
66
- # Make sure path is not already side loaded.
67
- unless Proscenium::Current.loaded[type].include?(resolved_path)
68
- 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
69
21
  end
70
-
71
- resolved_path
72
22
  end
73
23
  end
74
-
75
- private
76
-
77
- def log(...)
78
- self.class.log(...)
79
- end
80
-
81
- def resolve_path(path)
82
- path.exist? ? Utils.resolve_path(path.to_s) : nil
83
- end
84
24
  end
85
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.10.0'
4
+ VERSION = '0.11.0.pre.2'
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,22 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- #
4
- # Renders a <div> for use with React components, with data attributes specifying the component path
5
- # 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 that
9
- # the component is mounted to this div, and the loading UI will then be replaced with the
10
- # component's rendered output.
11
- #
12
- class Proscenium::ViewComponent::ReactComponent < Proscenium::ViewComponent
13
- 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
14
13
 
15
- include Proscenium::Componentable
14
+ include ReactComponentable
16
15
 
17
- def call
18
- tag.send root_tag, data: data_attributes do
19
- tag.div content || 'loading...'
16
+ def call
17
+ tag.send root_tag, data: data_attributes do
18
+ tag.div content || 'loading...'
19
+ end
20
20
  end
21
21
  end
22
22
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::ViewComponent::Sideload
4
+ end
@@ -4,59 +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. Additionally, if the class
14
- # responds to `side_load`, then that method is called.
15
15
  module Sideload
16
16
  def before_render
17
- klass = self.class
18
-
19
- if !klass.abstract_class && respond_to?(:side_load, true)
20
- side_load
21
- klass = klass.superclass
22
- end
23
-
24
- while !klass.abstract_class && klass.respond_to?(:path) && klass.path
25
- Proscenium::SideLoad.append klass.path
26
- klass = klass.superclass
27
- end
17
+ Proscenium::SideLoad.sideload_inheritance_chain self
28
18
 
29
19
  super
30
20
  end
31
21
  end
32
22
 
33
23
  class << self
34
- attr_accessor :path, :abstract_class
24
+ attr_accessor :abstract_class
35
25
 
36
26
  def inherited(child)
37
- child.path = if caller_locations(1, 1).first.label == 'inherited'
38
- Pathname.new caller_locations(2, 1).first.path
39
- else
40
- Pathname.new caller_locations(1, 1).first.path
41
- end
42
-
43
- child.prepend Sideload if Rails.application.config.proscenium.side_load
27
+ child.prepend Sideload
44
28
 
45
29
  super
46
30
  end
47
31
  end
48
-
49
- # @override Auto compilation of class names to css modules.
50
- def render_in(...)
51
- cssm.compile_class_names(super(...))
52
- end
53
-
54
- private
55
-
56
- # Overrides ActionView::Helpers::TagHelper::TagBuilder, allowing us to intercept the
57
- # `css_module` option from the HTML options argument of the `tag` and `content_tag` helpers, and
58
- # prepend it to the HTML `class` attribute.
59
- def tag_builder
60
- @tag_builder ||= Proscenium::ViewComponent::TagBuilder.new(self)
61
- end
62
32
  end
data/lib/proscenium.rb CHANGED
@@ -5,19 +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
12
- autoload :Componentable
24
+ autoload :ReactComponentable
13
25
  autoload :ViewComponent
14
26
  autoload :Phlex
15
27
  autoload :Helper
16
28
  autoload :Builder
17
-
18
- def self.reset_current_side_loaded
19
- Current.reset
20
- Current.loaded = SideLoad::EXTENSIONS.to_h { |e| [e, Set.new] }
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
21
38
  end
22
39
 
23
40
  class PathResolutionFailed < StandardError
@@ -30,68 +47,6 @@ module Proscenium
30
47
  "Path #{@path.inspect} cannot be resolved"
31
48
  end
32
49
  end
33
-
34
- module Utils
35
- module_function
36
-
37
- # @param value [#to_s] The value to create the digest from. This will usually be a `Pathname`.
38
- # @return [String] string digest of the given value.
39
- def digest(value)
40
- Digest::SHA1.hexdigest(value.to_s)[..7]
41
- end
42
-
43
- # Resolve the given `path` to a URL path.
44
- #
45
- # @param path [String] Can be URL path, file system path, or bare specifier (ie. NPM package).
46
- # @return [String] URL path.
47
- def resolve_path(path) # rubocop:disable Metrics/AbcSize
48
- raise ArgumentError, 'path must be a string' unless path.is_a?(String)
49
-
50
- if path.starts_with?('./', '../')
51
- raise ArgumentError, 'path must be an absolute file system or URL path'
52
- end
53
-
54
- matched_gem = Proscenium.config.side_load_gems.find do |_, opts|
55
- path.starts_with?("#{opts[:root]}/")
56
- end
57
-
58
- if matched_gem
59
- sroot = "#{matched_gem[1][:root]}/"
60
- relpath = path.delete_prefix(sroot)
61
-
62
- if (package_name = matched_gem[1][:package_name] || matched_gem[0])
63
- return Builder.resolve("#{package_name}/#{relpath}")
64
- end
65
-
66
- # TODO: manually resolve the path without esbuild
67
- raise PathResolutionFailed, path
68
- end
69
-
70
- return path.delete_prefix(Rails.root.to_s) if path.starts_with?("#{Rails.root}/")
71
-
72
- Builder.resolve(path)
73
- end
74
-
75
- # Resolves CSS class `names` to CSS module names. Each name will be converted to a CSS module
76
- # name, consisting of the camelCased name (lower case first character), and suffixed with the
77
- # given `digest`.
78
- #
79
- # @param names [String, Array]
80
- # @param digest: [String]
81
- # @returns [Array] of class names generated from the given CSS module `names` and `digest`.
82
- def css_modularise_class_names(*names, digest: nil)
83
- names.flatten.compact.map { |name| css_modularise_class_name name, digest: digest }
84
- end
85
-
86
- def css_modularise_class_name(name, digest: nil)
87
- sname = name.to_s
88
- if sname.starts_with?('_')
89
- "_#{sname[1..].camelize(:lower)}#{digest}"
90
- else
91
- "#{sname.camelize(:lower)}#{digest}"
92
- end
93
- end
94
- end
95
50
  end
96
51
 
97
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.10.0
4
+ version: 0.11.0.pre.2
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Joel Moss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-15 00:00:00.000000000 Z
11
+ date: 2023-09-12 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
@@ -104,34 +90,40 @@ files:
104
90
  - README.md
105
91
  - lib/proscenium.rb
106
92
  - lib/proscenium/builder.rb
107
- - lib/proscenium/componentable.rb
108
93
  - lib/proscenium/css_module.rb
109
- - lib/proscenium/css_module/class_names_resolver.rb
110
- - lib/proscenium/css_module/resolver.rb
111
- - lib/proscenium/current.rb
94
+ - lib/proscenium/css_module/path.rb
95
+ - lib/proscenium/css_module/transformer.rb
96
+ - lib/proscenium/ensure_loaded.rb
112
97
  - lib/proscenium/ext/proscenium
113
98
  - lib/proscenium/ext/proscenium.h
114
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
115
103
  - lib/proscenium/libs/stimulus-loading.js
104
+ - lib/proscenium/libs/test.js
116
105
  - lib/proscenium/log_subscriber.rb
117
106
  - lib/proscenium/middleware.rb
118
107
  - lib/proscenium/middleware/base.rb
119
108
  - lib/proscenium/middleware/esbuild.rb
109
+ - lib/proscenium/middleware/runtime.rb
120
110
  - lib/proscenium/middleware/url.rb
111
+ - lib/proscenium/monkey.rb
121
112
  - lib/proscenium/phlex.rb
122
- - lib/proscenium/phlex/component_concerns.rb
113
+ - lib/proscenium/phlex/css_modules.rb
123
114
  - lib/proscenium/phlex/page.rb
124
115
  - lib/proscenium/phlex/react_component.rb
125
- - lib/proscenium/phlex/resolve_css_modules.rb
126
116
  - lib/proscenium/railtie.rb
117
+ - lib/proscenium/react_componentable.rb
118
+ - lib/proscenium/resolver.rb
127
119
  - lib/proscenium/side_load.rb
128
- - lib/proscenium/side_load/ensure_loaded.rb
129
- - lib/proscenium/side_load/helper.rb
130
- - lib/proscenium/side_load/monkey.rb
120
+ - lib/proscenium/source_path.rb
121
+ - lib/proscenium/utils.rb
131
122
  - lib/proscenium/version.rb
132
123
  - lib/proscenium/view_component.rb
124
+ - lib/proscenium/view_component/css_modules.rb
133
125
  - lib/proscenium/view_component/react_component.rb
134
- - lib/proscenium/view_component/tag_builder.rb
126
+ - lib/proscenium/view_component/sideload.rb
135
127
  homepage: https://github.com/joelmoss/proscenium
136
128
  licenses:
137
129
  - MIT
@@ -151,11 +143,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
151
143
  version: 2.7.0
152
144
  required_rubygems_version: !ruby/object:Gem::Requirement
153
145
  requirements:
154
- - - ">="
146
+ - - ">"
155
147
  - !ruby/object:Gem::Version
156
- version: '0'
148
+ version: 1.3.1
157
149
  requirements: []
158
- rubygems_version: 3.4.13
150
+ rubygems_version: 3.4.19
159
151
  signing_key:
160
152
  specification_version: 4
161
153
  summary: The engine powering your Rails frontend
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Proscenium::Componentable
4
- extend ActiveSupport::Concern
5
-
6
- included do
7
- # @return [Hash] the props to pass to the React component.
8
- attr_writer :props
9
-
10
- # The HTML tag to use as the wrapping element for the component. You can reassign this in your
11
- # component class to use a different tag:
12
- #
13
- # class MyComponent < Proscenium::ViewComponent::ReactComponent
14
- # self.root_tag = :span
15
- # end
16
- #
17
- # @return [Symbol]
18
- class_attribute :root_tag, instance_predicate: false, default: :div
19
-
20
- # Should the template block be forwarded as children to the React component?
21
- #
22
- # @return [Boolean]
23
- class_attribute :forward_children, default: false
24
- end
25
-
26
- # @param props: [Hash]
27
- def initialize(props: {})
28
- @props = props
29
-
30
- super()
31
- end
32
-
33
- def virtual_path
34
- Proscenium::Utils.resolve_path path.sub_ext('.jsx').to_s
35
- end
36
-
37
- private
38
-
39
- def data_attributes
40
- d = {
41
- proscenium_component_path: virtual_path,
42
- proscenium_component_props: prepared_props
43
- }
44
-
45
- d[:proscenium_component_forward_children] = true if forward_children?
46
-
47
- d
48
- end
49
-
50
- def props
51
- @props ||= {}
52
- end
53
-
54
- def prepared_props
55
- props.deep_transform_keys do |term|
56
- # This ensures that the first letter after a slash is not capitalized.
57
- string = term.to_s.split('/').map { |str| str.camelize :lower }.join('/')
58
-
59
- # Reverses the effect of ActiveSupport::Inflector.camelize converting slashes into `::`.
60
- string.gsub '::', '/'
61
- end.to_json
62
- end
63
- end