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'
         |