proscenium 0.10.0-x86_64-darwin → 0.11.0.pre.2-x86_64-darwin

Sign up to get free protection for your applications and to get access to all the features.
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-darwin
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