proscenium 0.10.0-aarch64-linux → 0.11.0-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.
- checksums.yaml +4 -4
- data/README.md +207 -45
- data/lib/proscenium/builder.rb +41 -19
- data/lib/proscenium/css_module/path.rb +31 -0
- data/lib/proscenium/css_module/transformer.rb +82 -0
- data/lib/proscenium/css_module.rb +12 -25
- data/lib/proscenium/ensure_loaded.rb +27 -0
- data/lib/proscenium/ext/proscenium +0 -0
- data/lib/proscenium/ext/proscenium.h +3 -2
- data/lib/proscenium/helper.rb +85 -0
- data/lib/proscenium/importer.rb +110 -0
- data/lib/proscenium/libs/react-manager/index.jsx +101 -0
- data/lib/proscenium/libs/react-manager/react.js +2 -0
- data/lib/proscenium/libs/test.js +1 -0
- data/lib/proscenium/middleware/base.rb +7 -7
- data/lib/proscenium/middleware/engines.rb +37 -0
- data/lib/proscenium/middleware/esbuild.rb +3 -5
- data/lib/proscenium/middleware/runtime.rb +18 -0
- data/lib/proscenium/middleware.rb +14 -4
- data/lib/proscenium/{side_load/monkey.rb → monkey.rb} +19 -15
- data/lib/proscenium/phlex/{resolve_css_modules.rb → css_modules.rb} +28 -16
- data/lib/proscenium/phlex/react_component.rb +26 -27
- data/lib/proscenium/phlex.rb +11 -30
- data/lib/proscenium/railtie.rb +44 -46
- data/lib/proscenium/react_componentable.rb +95 -0
- data/lib/proscenium/resolver.rb +37 -0
- data/lib/proscenium/side_load.rb +13 -73
- data/lib/proscenium/source_path.rb +15 -0
- data/lib/proscenium/templates/rescues/build_error.html.erb +30 -0
- data/lib/proscenium/utils.rb +13 -0
- data/lib/proscenium/version.rb +1 -1
- data/lib/proscenium/view_component/css_modules.rb +11 -0
- data/lib/proscenium/view_component/react_component.rb +15 -15
- data/lib/proscenium/view_component/sideload.rb +4 -0
- data/lib/proscenium/view_component.rb +8 -38
- data/lib/proscenium.rb +22 -68
- metadata +23 -30
- data/lib/proscenium/componentable.rb +0 -63
- data/lib/proscenium/css_module/class_names_resolver.rb +0 -66
- data/lib/proscenium/css_module/resolver.rb +0 -76
- data/lib/proscenium/current.rb +0 -9
- data/lib/proscenium/phlex/component_concerns.rb +0 -9
- data/lib/proscenium/phlex/page.rb +0 -62
- data/lib/proscenium/side_load/ensure_loaded.rb +0 -25
- data/lib/proscenium/side_load/helper.rb +0 -41
- data/lib/proscenium/view_component/tag_builder.rb +0 -23
@@ -1,15 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Proscenium
|
4
|
-
module Phlex::
|
5
|
-
|
4
|
+
module Phlex::CssModules
|
5
|
+
include Proscenium::CssModule
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
def self.included(base)
|
8
|
+
base.extend CssModule::Path
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# Set of CSS module paths that have been resolved after being transformed from 'class' HTML
|
14
|
+
# attributes. See #process_attributes. This is here because Phlex caches attributes. Which
|
15
|
+
# means while the CSS class names will be transformed, any resolved paths will be lost in
|
16
|
+
# subsequent requests.
|
17
|
+
attr_accessor :resolved_css_module_paths
|
9
18
|
end
|
10
19
|
|
11
20
|
def before_template
|
12
|
-
self.class.
|
21
|
+
self.class.resolved_css_module_paths ||= Concurrent::Set.new
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def after_template
|
26
|
+
self.class.resolved_css_module_paths.each do |path|
|
27
|
+
Proscenium::Importer.import path
|
28
|
+
end
|
29
|
+
|
13
30
|
super
|
14
31
|
end
|
15
32
|
|
@@ -44,24 +61,19 @@ module Proscenium
|
|
44
61
|
# end
|
45
62
|
# end
|
46
63
|
#
|
47
|
-
# The given class name should be underscored, and the resulting CSS module name will be
|
48
|
-
# `camelCased` with a lower case first character.
|
49
|
-
#
|
50
64
|
# @raise [Proscenium::CssModule::Resolver::NotFound] If a CSS module file is not found for the
|
51
65
|
# Phlex class file path.
|
52
66
|
def process_attributes(**attributes)
|
53
67
|
if attributes.key?(:class) && (attributes[:class] = tokens(attributes[:class])).include?('@')
|
54
|
-
|
55
|
-
|
56
|
-
attributes[:class] =
|
68
|
+
names = attributes[:class].is_a?(Array) ? attributes[:class] : attributes[:class].split
|
69
|
+
|
70
|
+
attributes[:class] = cssm.class_names(*names).map do |name, path|
|
71
|
+
self.class.resolved_css_module_paths << path if path
|
72
|
+
name
|
73
|
+
end
|
57
74
|
end
|
58
75
|
|
59
76
|
attributes
|
60
77
|
end
|
61
|
-
|
62
|
-
def after_template
|
63
|
-
super
|
64
|
-
self.class.side_load_cache&.each { |path| SideLoad.append! path, :css }
|
65
|
-
end
|
66
78
|
end
|
67
79
|
end
|
@@ -1,33 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
# Renders a <div> for use with React components, with data attributes specifying the component
|
5
|
-
# and props.
|
6
|
-
#
|
7
|
-
# If a block is given, it will be yielded within the div, allowing for a custom "loading" UI. If no
|
8
|
-
# block is given, then a "loading..." text will be rendered. It is intended that the component is
|
9
|
-
# mounted to this div, and the loading UI will then be replaced with the component's rendered
|
10
|
-
# output.
|
11
|
-
#
|
12
|
-
# You can pass props to the component in the `:props` keyword argument.
|
13
|
-
#
|
14
|
-
class Proscenium::Phlex::ReactComponent < Proscenium::Phlex
|
15
|
-
self.abstract_class = true
|
16
|
-
|
17
|
-
include Proscenium::Componentable
|
18
|
-
include Proscenium::Phlex::ComponentConcerns::CssModules
|
19
|
-
|
20
|
-
# Override this to provide your own loading UI.
|
3
|
+
module Proscenium
|
4
|
+
# Renders a <div> for use with React components, with data attributes specifying the component
|
5
|
+
# path and props.
|
21
6
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# end
|
27
|
-
# end
|
7
|
+
# If a block is given, it will be yielded within the div, allowing for a custom "loading" UI. If
|
8
|
+
# no block is given, then a "loading..." text will be rendered. It is intended that the component
|
9
|
+
# is mounted to this div, and the loading UI will then be replaced with the component's rendered
|
10
|
+
# output.
|
28
11
|
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
12
|
+
# You can pass props to the component in the `:props` keyword argument.
|
13
|
+
class Phlex::ReactComponent < Phlex
|
14
|
+
self.abstract_class = true
|
15
|
+
|
16
|
+
include ReactComponentable
|
17
|
+
|
18
|
+
# Override this to provide your own loading UI.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# def template(**attributes, &block)
|
22
|
+
# super do
|
23
|
+
# 'Look at me! I am loading now...'
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @yield the given block to a `div` within the top level component div.
|
28
|
+
def template(**attributes, &block)
|
29
|
+
send root_tag, **{ data: data_attributes }.deep_merge(attributes), &block
|
30
|
+
end
|
32
31
|
end
|
33
32
|
end
|
data/lib/proscenium/phlex.rb
CHANGED
@@ -5,54 +5,35 @@ require 'phlex-rails'
|
|
5
5
|
module Proscenium
|
6
6
|
class Phlex < ::Phlex::HTML
|
7
7
|
extend ActiveSupport::Autoload
|
8
|
-
include Proscenium::CssModule
|
9
8
|
|
10
|
-
autoload :
|
9
|
+
autoload :CssModules
|
11
10
|
autoload :ReactComponent
|
12
|
-
autoload :ResolveCssModules
|
13
|
-
autoload :ComponentConcerns
|
14
11
|
|
15
12
|
extend ::Phlex::Rails::HelperMacros
|
16
13
|
include ::Phlex::Rails::Helpers::JavaScriptIncludeTag
|
17
14
|
include ::Phlex::Rails::Helpers::StyleSheetLinkTag
|
15
|
+
include Proscenium::SourcePath
|
16
|
+
include CssModules
|
18
17
|
|
19
|
-
define_output_helper :side_load_stylesheets
|
20
|
-
define_output_helper :
|
18
|
+
define_output_helper :side_load_stylesheets # deprecated
|
19
|
+
define_output_helper :include_stylesheets
|
20
|
+
define_output_helper :side_load_javascripts # deprecated
|
21
|
+
define_output_helper :include_javascripts
|
22
|
+
define_output_helper :declare_lazy_scripts
|
21
23
|
|
22
|
-
# Side loads the class, and its super classes that respond to `.path`. Assign the
|
23
|
-
# `abstract_class` class variable to any abstract class, and it will not be side loaded.
|
24
|
-
# Additionally, if the class responds to `side_load`, then that method is called.
|
25
24
|
module Sideload
|
26
25
|
def before_template
|
27
|
-
|
28
|
-
|
29
|
-
if !klass.abstract_class && respond_to?(:side_load, true)
|
30
|
-
side_load
|
31
|
-
klass = klass.superclass
|
32
|
-
end
|
33
|
-
|
34
|
-
while !klass.abstract_class && klass.respond_to?(:path) && klass.path
|
35
|
-
Proscenium::SideLoad.append klass.path
|
36
|
-
klass = klass.superclass
|
37
|
-
end
|
26
|
+
Proscenium::SideLoad.sideload_inheritance_chain self
|
38
27
|
|
39
28
|
super
|
40
29
|
end
|
41
30
|
end
|
42
31
|
|
43
32
|
class << self
|
44
|
-
attr_accessor :
|
33
|
+
attr_accessor :abstract_class
|
45
34
|
|
46
35
|
def inherited(child)
|
47
|
-
|
48
|
-
child.path = if caller_locations(1, 1).first.label == 'inherited'
|
49
|
-
Pathname.new caller_locations(2, 1).first.path
|
50
|
-
else
|
51
|
-
Pathname.new caller_locations(1, 1).first.path
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
child.prepend Sideload if Rails.application.config.proscenium.side_load
|
36
|
+
child.prepend Sideload
|
56
37
|
|
57
38
|
super
|
58
39
|
end
|
data/lib/proscenium/railtie.rb
CHANGED
@@ -6,14 +6,6 @@ require 'proscenium/log_subscriber'
|
|
6
6
|
ENV['RAILS_ENV'] = Rails.env
|
7
7
|
|
8
8
|
module Proscenium
|
9
|
-
FILE_EXTENSIONS = ['js', 'mjs', 'ts', 'jsx', 'tsx', 'css', 'js.map', 'mjs.map', 'jsx.map',
|
10
|
-
'ts.map', 'tsx.map', 'css.map'].freeze
|
11
|
-
|
12
|
-
APPLICATION_INCLUDE_PATHS = ['config', 'app/assets', 'app/views', 'lib', '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
9
|
class << self
|
18
10
|
def config
|
19
11
|
@config ||= Railtie.config.proscenium
|
@@ -26,66 +18,72 @@ module Proscenium
|
|
26
18
|
config.proscenium = ActiveSupport::OrderedOptions.new
|
27
19
|
config.proscenium.debug = false
|
28
20
|
config.proscenium.side_load = true
|
29
|
-
config.proscenium.code_splitting =
|
21
|
+
config.proscenium.code_splitting = true
|
22
|
+
|
23
|
+
# TODO: implement!
|
30
24
|
config.proscenium.cache_query_string = Rails.env.production? && ENV.fetch('REVISION', nil)
|
31
25
|
config.proscenium.cache_max_age = 2_592_000 # 30 days
|
32
|
-
config.proscenium.include_paths = Set.new(APPLICATION_INCLUDE_PATHS)
|
33
26
|
|
34
27
|
# List of environment variable names that should be passed to the builder, which will then be
|
35
28
|
# passed to esbuild's `Define` option. Being explicit about which environment variables are
|
36
29
|
# defined means a faster build, as esbuild will have less to do.
|
37
30
|
config.proscenium.env_vars = Set.new
|
38
31
|
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# Because side loading uses URL paths, any gem dependencies that side load assets will fail,
|
42
|
-
# because the URL path will be relative to the application's root, and not the gem's root. By
|
43
|
-
# specifying a list of gems that can be side loaded, Proscenium will be able to resolve the URL
|
44
|
-
# path to the gem's root, and side load the asset.
|
32
|
+
# Rails engines to expose and allow Proscenium to serve their assets.
|
45
33
|
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
34
|
+
# A Rails engine that has assets, can add Proscenium as a gem dependency, and then add itself
|
35
|
+
# to this list. Proscenium will then serve the engine's assets at the URL path beginning with
|
36
|
+
# the engine name.
|
49
37
|
#
|
50
38
|
# Example:
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
39
|
+
# class Gem1::Engine < ::Rails::Engine
|
40
|
+
# config.proscenium.engines << self
|
41
|
+
# end
|
42
|
+
config.proscenium.engines = Set.new
|
43
|
+
|
44
|
+
config.action_dispatch.rescue_templates = {
|
45
|
+
'Proscenium::Builder::BuildError' => 'build_error'
|
46
|
+
}
|
47
|
+
|
48
|
+
initializer 'proscenium.debugging' do
|
49
|
+
if Rails.gem_version >= Gem::Version.new('7.1.0')
|
50
|
+
tpl_path = root.join('lib', 'proscenium', 'templates').to_s
|
51
|
+
ActionDispatch::DebugView::RESCUES_TEMPLATE_PATHS << tpl_path
|
52
|
+
end
|
60
53
|
end
|
61
54
|
|
62
55
|
initializer 'proscenium.middleware' do |app|
|
63
|
-
app.middleware.insert_after ActionDispatch::Static,
|
64
|
-
app.middleware.insert_after ActionDispatch::Static, Rack::ETag, 'no-cache'
|
65
|
-
app.middleware.insert_after ActionDispatch::Static, Rack::ConditionalGet
|
56
|
+
app.middleware.insert_after ActionDispatch::Static, Middleware
|
57
|
+
# app.middleware.insert_after ActionDispatch::Static, Rack::ETag, 'no-cache'
|
58
|
+
# app.middleware.insert_after ActionDispatch::Static, Rack::ConditionalGet
|
66
59
|
end
|
67
60
|
|
68
|
-
initializer 'proscenium.
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
ActiveSupport.on_load(:action_view) do
|
73
|
-
ActionView::Base.include Proscenium::SideLoad::Helper
|
74
|
-
|
75
|
-
ActionView::TemplateRenderer.prepend SideLoad::Monkey::TemplateRenderer
|
76
|
-
ActionView::PartialRenderer.prepend SideLoad::Monkey::PartialRenderer
|
77
|
-
end
|
78
|
-
|
79
|
-
ActiveSupport.on_load(:action_controller) do
|
80
|
-
ActionController::Base.include Proscenium::SideLoad::EnsureLoaded
|
81
|
-
end
|
61
|
+
initializer 'proscenium.monkey_patches' do
|
62
|
+
ActiveSupport.on_load(:action_view) do
|
63
|
+
ActionView::TemplateRenderer.prepend Monkey::TemplateRenderer
|
64
|
+
ActionView::PartialRenderer.prepend Monkey::PartialRenderer
|
82
65
|
end
|
83
66
|
end
|
84
67
|
|
85
68
|
initializer 'proscenium.helper' do
|
86
69
|
ActiveSupport.on_load(:action_view) do
|
87
|
-
ActionView::Base.include
|
70
|
+
ActionView::Base.include Helper
|
71
|
+
end
|
72
|
+
|
73
|
+
ActiveSupport.on_load(:action_controller) do
|
74
|
+
ActionController::Base.include EnsureLoaded
|
88
75
|
end
|
89
76
|
end
|
90
77
|
end
|
91
78
|
end
|
79
|
+
|
80
|
+
if Rails.gem_version < Gem::Version.new('7.1.0')
|
81
|
+
class ActionDispatch::DebugView
|
82
|
+
def initialize(assigns)
|
83
|
+
tpl_path = Proscenium::Railtie.root.join('lib', 'proscenium', 'templates').to_s
|
84
|
+
paths = [RESCUES_TEMPLATE_PATH, tpl_path]
|
85
|
+
lookup_context = ActionView::LookupContext.new(paths)
|
86
|
+
super(lookup_context, assigns, nil)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,95 @@
|
|
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
|
+
|
42
|
+
# @return [String] the URL path to the component manager.
|
43
|
+
class_attribute :manager, default: '/@proscenium/react-manager/index.jsx'
|
44
|
+
end
|
45
|
+
|
46
|
+
class_methods do
|
47
|
+
def sideload
|
48
|
+
Importer.import manager
|
49
|
+
Importer.sideload source_path, lazy: true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param props: [Hash]
|
54
|
+
def initialize(lazy: self.class.lazy, loader: self.class.loader, props: {})
|
55
|
+
self.lazy = lazy
|
56
|
+
self.loader = loader
|
57
|
+
@props = props
|
58
|
+
end
|
59
|
+
|
60
|
+
# The absolute URL path to the javascript component.
|
61
|
+
def virtual_path
|
62
|
+
@virtual_path ||= Resolver.resolve self.class.source_path.sub_ext('.jsx').to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
def props
|
66
|
+
@props ||= {}
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def data_attributes
|
72
|
+
{
|
73
|
+
proscenium_component_path: Pathname.new(virtual_path).to_s,
|
74
|
+
proscenium_component_props: prepared_props,
|
75
|
+
proscenium_component_lazy: lazy
|
76
|
+
}.tap do |x|
|
77
|
+
x[:proscenium_component_forward_children] = true if forward_children?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def prepared_props
|
82
|
+
props.deep_transform_keys do |term|
|
83
|
+
# This ensures that the first letter after a slash is not capitalized.
|
84
|
+
string = term.to_s.split('/').map { |str| str.camelize :lower }.join('/')
|
85
|
+
|
86
|
+
# Reverses the effect of ActiveSupport::Inflector.camelize converting slashes into `::`.
|
87
|
+
string.gsub '::', '/'
|
88
|
+
end.to_json
|
89
|
+
end
|
90
|
+
|
91
|
+
def loader_component
|
92
|
+
render Loader::Component.new(loader, @html_class, data_attributes, tag: @html_tag)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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
|
+
#
|
15
|
+
# rubocop:disable Metrics/*
|
16
|
+
def self.resolve(path)
|
17
|
+
self.resolved ||= {}
|
18
|
+
|
19
|
+
self.resolved[path] ||= begin
|
20
|
+
if path.start_with?('./', '../')
|
21
|
+
raise ArgumentError, 'path must be an absolute file system or URL path'
|
22
|
+
end
|
23
|
+
|
24
|
+
if path.start_with?('@proscenium/')
|
25
|
+
"/#{path}"
|
26
|
+
elsif (engine = Proscenium.config.engines.find { |e| path.start_with? "#{e.root}/" })
|
27
|
+
path.sub(/^#{engine.root}/, "/#{engine.engine_name}")
|
28
|
+
elsif path.start_with?("#{Rails.root}/")
|
29
|
+
path.delete_prefix Rails.root.to_s
|
30
|
+
else
|
31
|
+
Builder.resolve path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# rubocop:enable Metrics/*
|
36
|
+
end
|
37
|
+
end
|
data/lib/proscenium/side_load.rb
CHANGED
@@ -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
|
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
|
-
#
|
31
|
-
|
32
|
-
|
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
|
-
#
|
39
|
-
#
|
40
|
-
def
|
41
|
-
return if Proscenium
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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,30 @@
|
|
1
|
+
<header>
|
2
|
+
<h1>
|
3
|
+
<%= Rails.gem_version >= Gem::Version.new('7.1.0') ? @exception_wrapper.exception_class_name : @exception.class.to_s %>
|
4
|
+
<% if params_valid? && @request.parameters['controller'] %>
|
5
|
+
in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %>
|
6
|
+
<% end %>
|
7
|
+
</h1>
|
8
|
+
</header>
|
9
|
+
|
10
|
+
<main role="main" id="container">
|
11
|
+
<%= render "rescues/message_and_suggestions", exception: @exception, exception_wrapper: Rails.gem_version >= Gem::Version.new('7.1.0') ? @exception_wrapper : nil %>
|
12
|
+
|
13
|
+
<% if @exception.error['location'] %>
|
14
|
+
<div class="source">
|
15
|
+
<div class="data">
|
16
|
+
<pre>
|
17
|
+
|
18
|
+
<%= @exception.error['location']['file'] %>:<%= @exception.error['location']['line'] %>:<%= @exception.error['location']['column'] %>
|
19
|
+
|
20
|
+
<%= @exception.error['location']['line'].to_s.rjust 5 %> │ <%= @exception.error['location']['line_text'] %>
|
21
|
+
│ <%= (@exception.error['location']['length'] > 1 ? "~" * @exception.error['location']['length'] : "^").rjust(@exception.error['location']['column'] + @exception.error['location']['length']) %>
|
22
|
+
<%- if @exception.error['location']['suggestion'].present? -%> + │ <%= @exception.error['location']['suggestion'].rjust(@exception.error['location']['column'] + 1) %>
|
23
|
+
<% else %> <%- end -%>
|
24
|
+
</pre>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
<%= render template: "rescues/_request_and_response" %>
|
30
|
+
</main>
|
@@ -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
|
data/lib/proscenium/version.rb
CHANGED