proscenium 0.13.0-x86_64-linux → 0.15.0.beta.1-x86_64-linux
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +63 -54
- data/lib/proscenium/builder.rb +62 -19
- data/lib/proscenium/css_module/transformer.rb +4 -2
- 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
data/lib/proscenium/importer.rb
CHANGED
@@ -72,8 +72,8 @@ module Proscenium
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
JS_EXTENSIONS.find(&import_if_exists)
|
76
|
-
CSS_EXTENSIONS.find(&import_if_exists)
|
75
|
+
JS_EXTENSIONS.find(&import_if_exists) unless options[:js] == false
|
76
|
+
CSS_EXTENSIONS.find(&import_if_exists) unless options[:css] == false
|
77
77
|
end
|
78
78
|
|
79
79
|
def each_stylesheet(delete: false)
|
@@ -109,10 +109,6 @@ module Proscenium
|
|
109
109
|
imported&.keys&.any? { |x| x.end_with?(*JS_EXTENSIONS) }
|
110
110
|
end
|
111
111
|
|
112
|
-
def multiple_js_imported?
|
113
|
-
imported&.keys&.many? { |x| x.end_with?(*JS_EXTENSIONS) }
|
114
|
-
end
|
115
|
-
|
116
112
|
def imported?(filepath = nil)
|
117
113
|
filepath ? imported&.key?(filepath) : !imported.blank?
|
118
114
|
end
|
@@ -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