proscenium 0.14.0-x86_64-darwin → 0.15.0.beta.1-x86_64-darwin
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 +63 -54
- data/lib/proscenium/builder.rb +62 -19
- data/lib/proscenium/ensure_loaded.rb +6 -6
- data/lib/proscenium/ext/proscenium +0 -0
- data/lib/proscenium/ext/proscenium.h +16 -1
- data/lib/proscenium/helper.rb +23 -70
- data/lib/proscenium/importer.rb +2 -6
- data/lib/proscenium/libs/react-manager/index.jsx +56 -36
- data/lib/proscenium/log_subscriber.rb +14 -2
- data/lib/proscenium/middleware/esbuild.rb +2 -2
- data/lib/proscenium/monkey.rb +67 -32
- data/lib/proscenium/phlex/asset_inclusions.rb +4 -83
- data/lib/proscenium/phlex.rb +9 -1
- data/lib/proscenium/railtie.rb +21 -16
- data/lib/proscenium/react_componentable.rb +3 -3
- data/lib/proscenium/side_load.rb +120 -6
- data/lib/proscenium/version.rb +1 -1
- data/lib/proscenium/view_component.rb +7 -1
- data/lib/proscenium.rb +10 -0
- metadata +3 -3
@@ -1,18 +1,70 @@
|
|
1
1
|
window.Proscenium = window.Proscenium || { lazyScripts: {} };
|
2
|
+
const pathAttribute = "data-proscenium-component-path";
|
2
3
|
|
4
|
+
// Find lazyscripts JSON already in the DOM.
|
3
5
|
const element = document.querySelector("#prosceniumLazyScripts");
|
4
6
|
if (element) {
|
5
|
-
const scripts = JSON.parse(element.text);
|
6
7
|
window.Proscenium.lazyScripts = {
|
7
8
|
...window.Proscenium.lazyScripts,
|
8
|
-
...
|
9
|
+
...JSON.parse(element.text),
|
9
10
|
};
|
10
11
|
}
|
11
12
|
|
12
|
-
|
13
|
+
// Find components already in the DOM.
|
14
|
+
const elements = document.querySelectorAll(`[${pathAttribute}]`);
|
13
15
|
elements.length > 0 && init(elements);
|
14
16
|
|
15
|
-
|
17
|
+
new MutationObserver((mutationsList) => {
|
18
|
+
for (const { addedNodes } of mutationsList) {
|
19
|
+
for (const ele of addedNodes) {
|
20
|
+
if (ele.tagName === "SCRIPT" && ele.id === "prosceniumLazyScripts") {
|
21
|
+
window.Proscenium.lazyScripts = {
|
22
|
+
...window.Proscenium.lazyScripts,
|
23
|
+
...JSON.parse(ele.text),
|
24
|
+
};
|
25
|
+
} else if (ele.matches(`[${pathAttribute}]`)) {
|
26
|
+
init([ele]);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}).observe(document, {
|
31
|
+
subtree: true,
|
32
|
+
childList: true,
|
33
|
+
});
|
34
|
+
|
35
|
+
function init(elements) {
|
36
|
+
Array.from(elements, (element) => {
|
37
|
+
const path = element.dataset.prosceniumComponentPath;
|
38
|
+
const isLazy = "prosceniumComponentLazy" in element.dataset;
|
39
|
+
const props = JSON.parse(element.dataset.prosceniumComponentProps);
|
40
|
+
|
41
|
+
if (proscenium.env.RAILS_ENV === "development") {
|
42
|
+
console.groupCollapsed(
|
43
|
+
`[proscenium/react/manager] ${isLazy ? "💤" : "⚡️"} %o`,
|
44
|
+
path
|
45
|
+
);
|
46
|
+
console.log("element: %o", element);
|
47
|
+
console.log("props: %o", props);
|
48
|
+
console.groupEnd();
|
49
|
+
}
|
50
|
+
|
51
|
+
if (isLazy) {
|
52
|
+
const observer = new IntersectionObserver((entries) => {
|
53
|
+
entries.forEach((entry) => {
|
54
|
+
if (entry.isIntersecting) {
|
55
|
+
observer.unobserve(element);
|
56
|
+
|
57
|
+
mount(element, path, props);
|
58
|
+
}
|
59
|
+
});
|
60
|
+
});
|
61
|
+
|
62
|
+
observer.observe(element);
|
63
|
+
} else {
|
64
|
+
mount(element, path, props);
|
65
|
+
}
|
66
|
+
});
|
67
|
+
|
16
68
|
/**
|
17
69
|
* Mounts component located at `path`, into the DOM `element`.
|
18
70
|
*
|
@@ -66,36 +118,4 @@ function init() {
|
|
66
118
|
console.error("[proscenium/react/manager] %o - %o", path, error);
|
67
119
|
});
|
68
120
|
}
|
69
|
-
|
70
|
-
Array.from(elements, (element) => {
|
71
|
-
const path = element.dataset.prosceniumComponentPath;
|
72
|
-
const isLazy = "prosceniumComponentLazy" in element.dataset;
|
73
|
-
const props = JSON.parse(element.dataset.prosceniumComponentProps);
|
74
|
-
|
75
|
-
if (proscenium.env.RAILS_ENV === "development") {
|
76
|
-
console.groupCollapsed(
|
77
|
-
`[proscenium/react/manager] ${isLazy ? "💤" : "⚡️"} %o`,
|
78
|
-
path
|
79
|
-
);
|
80
|
-
console.log("element: %o", element);
|
81
|
-
console.log("props: %o", props);
|
82
|
-
console.groupEnd();
|
83
|
-
}
|
84
|
-
|
85
|
-
if (isLazy) {
|
86
|
-
const observer = new IntersectionObserver((entries) => {
|
87
|
-
entries.forEach((entry) => {
|
88
|
-
if (entry.isIntersecting) {
|
89
|
-
observer.unobserve(element);
|
90
|
-
|
91
|
-
mount(element, path, props);
|
92
|
-
}
|
93
|
-
});
|
94
|
-
});
|
95
|
-
|
96
|
-
observer.observe(element);
|
97
|
-
} else {
|
98
|
-
mount(element, path, props);
|
99
|
-
}
|
100
|
-
});
|
101
121
|
}
|
@@ -10,12 +10,24 @@ module Proscenium
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def build_to_path(event)
|
14
14
|
path = event.payload[:identifier]
|
15
|
+
cached = event.payload[:cached] ? ' | Cached!' : ''
|
15
16
|
path = CGI.unescape(path) if path.start_with?(/https?%3A%2F%2F/)
|
16
17
|
|
17
18
|
info do
|
18
|
-
message = +"[Proscenium] Building #{path}"
|
19
|
+
message = +" #{color('[Proscenium]', nil, bold: true)} Building (to path) #{path}"
|
20
|
+
message << " (Duration: #{event.duration.round(1)}ms | " \
|
21
|
+
"Allocations: #{event.allocations}#{cached})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_to_string(event)
|
26
|
+
path = event.payload[:identifier]
|
27
|
+
path = CGI.unescape(path) if path.start_with?(/https?%3A%2F%2F/)
|
28
|
+
|
29
|
+
info do
|
30
|
+
message = +" #{color('[Proscenium]', nil, bold: true)} Building #{path}"
|
19
31
|
message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
|
20
32
|
end
|
21
33
|
end
|
@@ -20,8 +20,8 @@ module Proscenium
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def attempt
|
23
|
-
render_response Builder.
|
24
|
-
|
23
|
+
render_response Builder.build_to_string(path_to_build, root: Rails.root.to_s,
|
24
|
+
base_url: @request.base_url)
|
25
25
|
rescue Builder::CompileError => e
|
26
26
|
raise self.class::CompileError, { file: @request.fullpath, detail: e.message }, caller
|
27
27
|
end
|
data/lib/proscenium/monkey.rb
CHANGED
@@ -3,42 +3,52 @@
|
|
3
3
|
module Proscenium
|
4
4
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
5
5
|
module Monkey
|
6
|
-
module DebugView
|
7
|
-
def initialize(assigns)
|
8
|
-
paths = [RESCUES_TEMPLATE_PATH, Rails.root.join('lib', 'templates').to_s]
|
9
|
-
lookup_context = ActionView::LookupContext.new(paths)
|
10
|
-
super(lookup_context, assigns, nil)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
6
|
module TemplateRenderer
|
15
7
|
private
|
16
8
|
|
17
|
-
def render_template(view, template, layout_name, locals)
|
18
|
-
|
9
|
+
def render_template(view, template, layout_name, locals) # rubocop:disable Metrics/*
|
10
|
+
result = super
|
11
|
+
return result if !view.controller || !Proscenium.config.side_load
|
19
12
|
|
20
|
-
layout = find_layout(layout_name, locals.keys, [formats.first])
|
21
13
|
renderable = template.instance_variable_get(:@renderable)
|
22
14
|
|
23
|
-
if Object.const_defined?(:ViewComponent) &&
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
15
|
+
to_sideload = if Object.const_defined?(:ViewComponent) &&
|
16
|
+
template.is_a?(ActionView::Template::Renderable) &&
|
17
|
+
renderable.class < ::ViewComponent::Base &&
|
18
|
+
renderable.class.format == :html
|
19
|
+
renderable
|
20
|
+
elsif template.respond_to?(:virtual_path) &&
|
21
|
+
template.respond_to?(:type) && template.type == :html
|
22
|
+
template
|
23
|
+
end
|
24
|
+
if to_sideload
|
25
|
+
options = view.controller.sideload_assets_options
|
26
|
+
layout = find_layout(layout_name, locals.keys, [formats.first])
|
27
|
+
sideload_template_assets layout, view.controller, options if layout
|
28
|
+
sideload_template_assets to_sideload, view.controller, options
|
29
|
+
end
|
30
|
+
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
def sideload_template_assets(tpl, controller, options)
|
35
|
+
options = {} if options.nil?
|
36
|
+
options = { js: options, css: options } unless options.is_a?(Hash)
|
37
|
+
|
38
|
+
if tpl.instance_variable_defined?(:@sideload_assets_options)
|
39
|
+
tpl_options = tpl.instance_variable_get(:@sideload_assets_options)
|
40
|
+
options = case tpl_options
|
41
|
+
when Hash then options.deep_merge(tpl_options)
|
42
|
+
else
|
43
|
+
{ js: tpl_options, css: tpl_options }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
%i[css js].each do |k|
|
48
|
+
options[k] = controller.instance_eval(&options[k]) if options[k].is_a?(Proc)
|
39
49
|
end
|
40
50
|
|
41
|
-
|
51
|
+
Importer.sideload "app/views/#{tpl.virtual_path}", **options
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
@@ -46,13 +56,38 @@ module Proscenium
|
|
46
56
|
private
|
47
57
|
|
48
58
|
def render_partial_template(view, locals, template, layout, block)
|
49
|
-
|
59
|
+
result = super
|
60
|
+
|
61
|
+
return result if !view.controller || !Proscenium.config.side_load
|
62
|
+
|
63
|
+
if template.respond_to?(:virtual_path) &&
|
50
64
|
template.respond_to?(:type) && template.type == :html
|
51
|
-
|
52
|
-
|
65
|
+
options = view.controller.sideload_assets_options
|
66
|
+
sideload_template_assets layout, options if layout
|
67
|
+
sideload_template_assets template, options
|
68
|
+
end
|
69
|
+
|
70
|
+
result
|
71
|
+
end
|
72
|
+
|
73
|
+
def sideload_template_assets(tpl, options)
|
74
|
+
options = {} if options.nil?
|
75
|
+
options = { js: options, css: options } unless options.is_a?(Hash)
|
76
|
+
|
77
|
+
if tpl.instance_variable_defined?(:@sideload_assets_options)
|
78
|
+
tpl_options = tpl.instance_variable_get(:@sideload_assets_options)
|
79
|
+
options = if tpl_options.is_a?(Hash)
|
80
|
+
options.deep_merge tpl_options
|
81
|
+
else
|
82
|
+
{ js: tpl_options, css: tpl_options }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
%i[css js].each do |k|
|
87
|
+
options[k] = controller.instance_eval(&options[k]) if options[k].is_a?(Proc)
|
53
88
|
end
|
54
89
|
|
55
|
-
|
90
|
+
Importer.sideload "app/views/#{tpl.virtual_path}", **options
|
56
91
|
end
|
57
92
|
end
|
58
93
|
end
|
@@ -1,96 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Proscenium::Phlex::AssetInclusions
|
4
|
-
include Phlex::Rails::Helpers::ContentFor
|
5
|
-
include Phlex::Rails::Helpers::StyleSheetPath
|
6
|
-
include Phlex::Rails::Helpers::JavaScriptPath
|
7
|
-
|
8
4
|
def include_stylesheets
|
9
5
|
comment { '[PROSCENIUM_STYLESHEETS]' }
|
10
6
|
end
|
11
7
|
|
12
|
-
def include_javascripts
|
13
|
-
comment { '[PROSCENIUM_JAVASCRIPTS]' }
|
14
|
-
!defer_lazy_scripts && include_lazy_javascripts
|
15
|
-
end
|
16
|
-
|
17
|
-
def include_lazy_javascripts
|
8
|
+
def include_javascripts
|
18
9
|
comment { '[PROSCENIUM_LAZY_SCRIPTS]' }
|
10
|
+
comment { '[PROSCENIUM_JAVASCRIPTS]' }
|
19
11
|
end
|
20
12
|
|
21
|
-
def include_assets
|
13
|
+
def include_assets
|
22
14
|
include_stylesheets
|
23
|
-
include_javascripts
|
24
|
-
end
|
25
|
-
|
26
|
-
def after_template
|
27
|
-
super
|
28
|
-
|
29
|
-
@_buffer.gsub! '<!-- [PROSCENIUM_STYLESHEETS] -->', capture_stylesheets!
|
30
|
-
@_buffer.gsub! '<!-- [PROSCENIUM_JAVASCRIPTS] -->', capture_javascripts!
|
31
|
-
|
32
|
-
if content_for?(:proscenium_lazy_scripts)
|
33
|
-
flush
|
34
|
-
@_buffer.gsub!('<!-- [PROSCENIUM_LAZY_SCRIPTS] -->', capture do
|
35
|
-
content_for(:proscenium_lazy_scripts)
|
36
|
-
end)
|
37
|
-
else
|
38
|
-
@_buffer.gsub! '<!-- [PROSCENIUM_LAZY_SCRIPTS] -->', ''
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def capture_stylesheets!
|
45
|
-
capture do
|
46
|
-
Proscenium::Importer.each_stylesheet(delete: true) do |path, _path_options|
|
47
|
-
link rel: 'stylesheet', href: stylesheet_path(path, extname: false)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def capture_javascripts! # rubocop:disable Metrics/*
|
53
|
-
unless Rails.application.config.proscenium.code_splitting &&
|
54
|
-
Proscenium::Importer.multiple_js_imported?
|
55
|
-
return capture do
|
56
|
-
Proscenium::Importer.each_javascript(delete: true) do |path, _|
|
57
|
-
script(src: javascript_path(path, extname: false), type: :module)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
imports = Proscenium::Importer.imported.dup
|
63
|
-
paths_to_build = []
|
64
|
-
Proscenium::Importer.each_javascript(delete: true) do |x, _|
|
65
|
-
paths_to_build << x.delete_prefix('/')
|
66
|
-
end
|
67
|
-
|
68
|
-
result = Proscenium::Builder.build(paths_to_build.join(';'), base_url: helpers.request.base_url)
|
69
|
-
|
70
|
-
# Remove the react components from the results, so they are not side loaded. Instead they
|
71
|
-
# are lazy loaded by the component manager.
|
72
|
-
|
73
|
-
capture do
|
74
|
-
scripts = {}
|
75
|
-
result.split(';').each do |x|
|
76
|
-
inpath, outpath = x.split('::')
|
77
|
-
inpath.prepend '/'
|
78
|
-
outpath.delete_prefix! 'public'
|
79
|
-
|
80
|
-
next unless imports.key?(inpath)
|
81
|
-
|
82
|
-
if (import = imports[inpath]).delete(:lazy)
|
83
|
-
scripts[inpath] = import.merge(outpath: outpath)
|
84
|
-
else
|
85
|
-
script(src: javascript_path(outpath, extname: false), type: :module)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
content_for :proscenium_lazy_scripts do
|
90
|
-
script type: 'application/json', id: 'prosceniumLazyScripts' do
|
91
|
-
unsafe_raw scripts.to_json
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
15
|
+
include_javascripts
|
95
16
|
end
|
96
17
|
end
|
data/lib/proscenium/phlex.rb
CHANGED
@@ -12,15 +12,19 @@ module Proscenium
|
|
12
12
|
|
13
13
|
include Proscenium::SourcePath
|
14
14
|
include CssModules
|
15
|
+
include AssetInclusions
|
15
16
|
|
16
17
|
module Sideload
|
17
18
|
def before_template
|
18
|
-
Proscenium::SideLoad.sideload_inheritance_chain self
|
19
|
+
Proscenium::SideLoad.sideload_inheritance_chain self,
|
20
|
+
helpers.controller.sideload_assets_options
|
19
21
|
|
20
22
|
super
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
26
|
+
class_attribute :sideload_assets_options
|
27
|
+
|
24
28
|
class << self
|
25
29
|
attr_accessor :abstract_class
|
26
30
|
|
@@ -29,6 +33,10 @@ module Proscenium
|
|
29
33
|
|
30
34
|
super
|
31
35
|
end
|
36
|
+
|
37
|
+
def sideload_assets(value)
|
38
|
+
self.sideload_assets_options = value
|
39
|
+
end
|
32
40
|
end
|
33
41
|
end
|
34
42
|
end
|
data/lib/proscenium/railtie.rb
CHANGED
@@ -6,12 +6,6 @@ require 'proscenium/log_subscriber'
|
|
6
6
|
ENV['RAILS_ENV'] = Rails.env
|
7
7
|
|
8
8
|
module Proscenium
|
9
|
-
class << self
|
10
|
-
def config
|
11
|
-
@config ||= Railtie.config.proscenium
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
9
|
class Railtie < ::Rails::Engine
|
16
10
|
isolate_namespace Proscenium
|
17
11
|
|
@@ -20,6 +14,14 @@ module Proscenium
|
|
20
14
|
config.proscenium.side_load = true
|
21
15
|
config.proscenium.code_splitting = true
|
22
16
|
|
17
|
+
# Cache asset paths when building to path. Enabled by default in production.
|
18
|
+
# @see Proscenium::Builder#build_to_path
|
19
|
+
config.proscenium.cache = if Rails.env.local?
|
20
|
+
ActiveSupport::Cache::NullStore.new
|
21
|
+
else
|
22
|
+
ActiveSupport::Cache::MemoryStore.new
|
23
|
+
end
|
24
|
+
|
23
25
|
# TODO: implement!
|
24
26
|
config.proscenium.cache_query_string = Rails.env.production? && ENV.fetch('REVISION', nil)
|
25
27
|
config.proscenium.cache_max_age = 2_592_000 # 30 days
|
@@ -45,6 +47,12 @@ module Proscenium
|
|
45
47
|
'Proscenium::Builder::BuildError' => 'build_error'
|
46
48
|
}
|
47
49
|
|
50
|
+
config.after_initialize do |_app|
|
51
|
+
ActiveSupport.on_load(:action_view) do
|
52
|
+
include Proscenium::Helper
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
48
56
|
initializer 'proscenium.debugging' do
|
49
57
|
if Rails.gem_version >= Gem::Version.new('7.1.0')
|
50
58
|
tpl_path = root.join('lib', 'proscenium', 'templates').to_s
|
@@ -58,20 +66,17 @@ module Proscenium
|
|
58
66
|
app.middleware.insert_after ActionDispatch::Static, Rack::ConditionalGet
|
59
67
|
end
|
60
68
|
|
61
|
-
initializer 'proscenium.
|
62
|
-
ActiveSupport.on_load(:
|
63
|
-
|
64
|
-
|
69
|
+
initializer 'proscenium.sideloading' do
|
70
|
+
ActiveSupport.on_load(:action_controller) do
|
71
|
+
ActionController::Base.include EnsureLoaded
|
72
|
+
ActionController::Base.include SideLoad::Controller
|
65
73
|
end
|
66
74
|
end
|
67
75
|
|
68
|
-
initializer 'proscenium.
|
76
|
+
initializer 'proscenium.monkey_patches' do
|
69
77
|
ActiveSupport.on_load(:action_view) do
|
70
|
-
ActionView::
|
71
|
-
|
72
|
-
|
73
|
-
ActiveSupport.on_load(:action_controller) do
|
74
|
-
ActionController::Base.include EnsureLoaded
|
78
|
+
ActionView::TemplateRenderer.prepend Monkey::TemplateRenderer
|
79
|
+
ActionView::PartialRenderer.prepend Monkey::PartialRenderer
|
75
80
|
end
|
76
81
|
end
|
77
82
|
end
|
@@ -44,9 +44,9 @@ module Proscenium
|
|
44
44
|
end
|
45
45
|
|
46
46
|
class_methods do
|
47
|
-
def sideload
|
48
|
-
Importer.import manager
|
49
|
-
Importer.sideload source_path, lazy: true
|
47
|
+
def sideload(options)
|
48
|
+
Importer.import manager, **options, js: { type: 'module' }
|
49
|
+
Importer.sideload source_path, lazy: true, **options
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
data/lib/proscenium/side_load.rb
CHANGED
@@ -2,21 +2,135 @@
|
|
2
2
|
|
3
3
|
module Proscenium
|
4
4
|
class SideLoad
|
5
|
+
module Controller
|
6
|
+
def self.included(child)
|
7
|
+
child.class_eval do
|
8
|
+
class_attribute :sideload_assets_options
|
9
|
+
child.extend ClassMethods
|
10
|
+
|
11
|
+
append_after_action :capture_and_replace_proscenium_stylesheets,
|
12
|
+
:capture_and_replace_proscenium_javascripts,
|
13
|
+
if: -> { request.format.html? && !response.redirect? }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def sideload_assets(value)
|
19
|
+
self.sideload_assets_options = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def capture_and_replace_proscenium_stylesheets # rubocop:disable Metrics/AbcSize
|
24
|
+
return if response_body.first.blank? || !Proscenium::Importer.css_imported?
|
25
|
+
return unless response_body.first.include? '<!-- [PROSCENIUM_STYLESHEETS] -->'
|
26
|
+
|
27
|
+
imports = Proscenium::Importer.imported.dup
|
28
|
+
paths_to_build = []
|
29
|
+
Proscenium::Importer.each_stylesheet(delete: true) do |x, _|
|
30
|
+
paths_to_build << x.delete_prefix('/')
|
31
|
+
end
|
32
|
+
|
33
|
+
result = Proscenium::Builder.build_to_path(paths_to_build.join(';'),
|
34
|
+
base_url: helpers.request.base_url)
|
35
|
+
|
36
|
+
out = []
|
37
|
+
result.split(';').each do |x|
|
38
|
+
inpath, outpath = x.split('::')
|
39
|
+
inpath.prepend '/'
|
40
|
+
outpath.delete_prefix! 'public'
|
41
|
+
|
42
|
+
next unless imports.key?(inpath)
|
43
|
+
|
44
|
+
import = imports[inpath]
|
45
|
+
opts = import[:css].is_a?(Hash) ? import[:css] : {}
|
46
|
+
out << helpers.stylesheet_link_tag(outpath, extname: false, **opts)
|
47
|
+
end
|
48
|
+
|
49
|
+
response_body.first.gsub! '<!-- [PROSCENIUM_STYLESHEETS] -->', out.join.html_safe
|
50
|
+
end
|
51
|
+
|
52
|
+
def capture_and_replace_proscenium_javascripts # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/PerceivedComplexity
|
53
|
+
return if response_body.first.blank? || !Proscenium::Importer.js_imported?
|
54
|
+
|
55
|
+
imports = Proscenium::Importer.imported.dup
|
56
|
+
paths_to_build = []
|
57
|
+
Proscenium::Importer.each_javascript(delete: true) do |x, _|
|
58
|
+
paths_to_build << x.delete_prefix('/')
|
59
|
+
end
|
60
|
+
|
61
|
+
result = Proscenium::Builder.build_to_path(paths_to_build.join(';'),
|
62
|
+
base_url: helpers.request.base_url)
|
63
|
+
|
64
|
+
if response_body.first.include? '<!-- [PROSCENIUM_JAVASCRIPTS] -->'
|
65
|
+
out = []
|
66
|
+
scripts = {}
|
67
|
+
result.split(';').each do |x|
|
68
|
+
inpath, outpath = x.split('::')
|
69
|
+
inpath.prepend '/'
|
70
|
+
outpath.delete_prefix! 'public'
|
71
|
+
|
72
|
+
next unless imports.key?(inpath)
|
73
|
+
|
74
|
+
if (import = imports[inpath]).delete(:lazy)
|
75
|
+
scripts[inpath] = import.merge(outpath: outpath)
|
76
|
+
else
|
77
|
+
opts = import[:js].is_a?(Hash) ? import[:js] : {}
|
78
|
+
out << helpers.javascript_include_tag(outpath, extname: false, **opts)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
response_body.first.gsub! '<!-- [PROSCENIUM_JAVASCRIPTS] -->', out.join.html_safe
|
83
|
+
end
|
84
|
+
|
85
|
+
return unless response_body.first.include? '<!-- [PROSCENIUM_LAZY_SCRIPTS] -->'
|
86
|
+
|
87
|
+
lazy_script = ''
|
88
|
+
if scripts.present?
|
89
|
+
lazy_script = helpers.content_tag 'script', type: 'application/json',
|
90
|
+
id: 'prosceniumLazyScripts' do
|
91
|
+
scripts.to_json.html_safe
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
response_body.first.gsub! '<!-- [PROSCENIUM_LAZY_SCRIPTS] -->', lazy_script
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
5
99
|
class << self
|
6
100
|
# Side loads the class, and its super classes that respond to `.source_path`.
|
7
101
|
#
|
8
|
-
#
|
9
|
-
# loaded.
|
10
|
-
# not be side loaded.
|
102
|
+
# Set the `abstract_class` class variable to true in any class, and it will not be side
|
103
|
+
# loaded.
|
11
104
|
#
|
12
105
|
# If the class responds to `.sideload`, it will be called instead of the regular side loading.
|
13
106
|
# You can use this to customise what is side loaded.
|
14
|
-
def sideload_inheritance_chain(obj)
|
15
|
-
return
|
107
|
+
def sideload_inheritance_chain(obj, options) # rubocop:disable Metrics/*
|
108
|
+
return unless Proscenium.config.side_load
|
109
|
+
|
110
|
+
options = {} if options.nil?
|
111
|
+
options = { js: options, css: options } unless options.is_a?(Hash)
|
112
|
+
|
113
|
+
unless obj.sideload_assets_options.nil?
|
114
|
+
tpl_options = obj.sideload_assets_options
|
115
|
+
options = if tpl_options.is_a?(Hash)
|
116
|
+
options.deep_merge tpl_options
|
117
|
+
else
|
118
|
+
{ js: tpl_options, css: tpl_options }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
%i[css js].each do |k|
|
123
|
+
options[k] = obj.instance_eval(&options[k]) if options[k].is_a?(Proc)
|
124
|
+
end
|
16
125
|
|
17
126
|
klass = obj.class
|
18
127
|
while klass.respond_to?(:source_path) && klass.source_path && !klass.abstract_class
|
19
|
-
klass.respond_to?(:sideload)
|
128
|
+
if klass.respond_to?(:sideload)
|
129
|
+
klass.sideload options
|
130
|
+
else
|
131
|
+
Importer.sideload klass.source_path, **options
|
132
|
+
end
|
133
|
+
|
20
134
|
klass = klass.superclass
|
21
135
|
end
|
22
136
|
end
|
data/lib/proscenium/version.rb
CHANGED
@@ -14,12 +14,14 @@ class Proscenium::ViewComponent < ViewComponent::Base
|
|
14
14
|
|
15
15
|
module Sideload
|
16
16
|
def before_render
|
17
|
-
Proscenium::SideLoad.sideload_inheritance_chain self
|
17
|
+
Proscenium::SideLoad.sideload_inheritance_chain self, controller.sideload_assets_options
|
18
18
|
|
19
19
|
super
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
class_attribute :sideload_assets_options
|
24
|
+
|
23
25
|
class << self
|
24
26
|
attr_accessor :abstract_class
|
25
27
|
|
@@ -28,5 +30,9 @@ class Proscenium::ViewComponent < ViewComponent::Base
|
|
28
30
|
|
29
31
|
super
|
30
32
|
end
|
33
|
+
|
34
|
+
def sideload_assets(value)
|
35
|
+
self.sideload_assets_options = value
|
36
|
+
end
|
31
37
|
end
|
32
38
|
end
|
data/lib/proscenium.rb
CHANGED
@@ -46,6 +46,16 @@ module Proscenium
|
|
46
46
|
"Path #{@path.inspect} cannot be resolved"
|
47
47
|
end
|
48
48
|
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
def config
|
52
|
+
@config ||= Railtie.config.proscenium
|
53
|
+
end
|
54
|
+
|
55
|
+
def cache
|
56
|
+
@cache ||= config.cache || ActiveSupport::Cache::NullStore.new
|
57
|
+
end
|
58
|
+
end
|
49
59
|
end
|
50
60
|
|
51
61
|
require 'proscenium/railtie'
|