lookbook 0.7.0 → 0.7.2.beta.1
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/app/assets/lookbook/css/app.css +19 -2
 - data/app/controllers/lookbook/pages_controller.rb +17 -5
 - data/app/controllers/lookbook/previews_controller.rb +35 -12
 - data/app/helpers/lookbook/component_helper.rb +4 -0
 - data/app/helpers/lookbook/page_helper.rb +0 -4
 - data/app/views/lookbook/components/_code.html.erb +9 -5
 - data/app/views/lookbook/components/_errors.html.erb +13 -0
 - data/app/views/lookbook/components/_preview.html.erb +1 -1
 - data/app/views/lookbook/components/_sidebar.html.erb +17 -3
 - data/app/views/lookbook/error.html.erb +46 -0
 - data/lib/lookbook/code_formatter.rb +6 -3
 - data/lib/lookbook/engine.rb +10 -11
 - data/lib/lookbook/error.rb +121 -0
 - data/lib/lookbook/page.rb +27 -13
 - data/lib/lookbook/preview.rb +68 -9
 - data/lib/lookbook/preview_example.rb +1 -1
 - data/lib/lookbook/version.rb +1 -1
 - data/lib/lookbook.rb +1 -0
 - data/public/lookbook-assets/css/app.css +1 -3
 - data/public/lookbook-assets/css/app.css.map +1 -1
 - data/public/lookbook-assets/js/app.js +1 -1
 - data/public/lookbook-assets/js/app.js.map +1 -1
 - data/public/lookbook-assets/js/embed.js +1 -1
 - data/public/lookbook-assets/js/embed.js.map +1 -1
 - metadata +7 -4
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 7c26bf5a4901bcb0fd09b8742ae3e9c2df8f8c337c3f312152cb31dc6a74aa23
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: e1e8a016b29cf87357c3b291c99eaaf8d1e21d1a4acb552bab7777699579f265
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: dd771d407e79c1958ad0c4873fa242d7c3ad345091291275c5efd261be1d4617d8e60a6a0e82da97f2e01da8aa3a4781f169c13b1559dd481a1939598585b8d1
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: d4a40a44f2864c7c9b7bdb3b849092973619044fae370e056dda42b7382bc324fcc6932b26b4a15962d283fa8030782bd3a37e0f1e33c50f4aaf5046ba5c0ccd
         
     | 
| 
         @@ -93,7 +93,7 @@ 
     | 
|
| 
       93 
93 
     | 
    
         
             
              .code.numbered:before {
         
     | 
| 
       94 
94 
     | 
    
         
             
                content: "";
         
     | 
| 
       95 
95 
     | 
    
         
             
                left: 2.7em;
         
     | 
| 
       96 
     | 
    
         
            -
                @apply absolute top-0 bottom-0 border-r border-gray-200;
         
     | 
| 
      
 96 
     | 
    
         
            +
                @apply absolute top-0 bottom-0 border-r border-gray-200 z-10;
         
     | 
| 
       97 
97 
     | 
    
         
             
              }
         
     | 
| 
       98 
98 
     | 
    
         | 
| 
       99 
99 
     | 
    
         
             
              .code.numbered .line {
         
     | 
| 
         @@ -115,8 +115,25 @@ 
     | 
|
| 
       115 
115 
     | 
    
         
             
                @apply flex-none pr-4;
         
     | 
| 
       116 
116 
     | 
    
         
             
              }
         
     | 
| 
       117 
117 
     | 
    
         | 
| 
      
 118 
     | 
    
         
            +
              .code.focussed .line:not(.highlighted-line) *,
         
     | 
| 
      
 119 
     | 
    
         
            +
              .code.focussed .line:not(.highlighted-line) .line-content * {
         
     | 
| 
      
 120 
     | 
    
         
            +
                @apply !text-gray-700 !text-opacity-40;
         
     | 
| 
      
 121 
     | 
    
         
            +
              }
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
              .code.focussed .line:not(.highlighted-line) .line-number {
         
     | 
| 
      
 124 
     | 
    
         
            +
                @apply !opacity-70;
         
     | 
| 
      
 125 
     | 
    
         
            +
              }
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
              .code.focussed .line.highlighted-line {
         
     | 
| 
      
 128 
     | 
    
         
            +
                @apply bg-yellow-50;
         
     | 
| 
      
 129 
     | 
    
         
            +
              }
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
              .code.focussed .line.highlighted-line .line-number {
         
     | 
| 
      
 132 
     | 
    
         
            +
                @apply text-gray-900;
         
     | 
| 
      
 133 
     | 
    
         
            +
              }
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
       118 
135 
     | 
    
         
             
              .prose .code {
         
     | 
| 
       119 
     | 
    
         
            -
                @apply !bg-white border border-gray-300 text-gray-600 my-8  
     | 
| 
      
 136 
     | 
    
         
            +
                @apply !bg-white border border-gray-300 text-gray-600 my-8 rounded-md py-4 text-sm overflow-auto max-w-3xl w-full mx-auto;
         
     | 
| 
       120 
137 
     | 
    
         
             
              }
         
     | 
| 
       121 
138 
     | 
    
         | 
| 
       122 
139 
     | 
    
         
             
              .prose .code:not(.numbered) .line {
         
     | 
| 
         @@ -18,12 +18,24 @@ module Lookbook 
     | 
|
| 
       18 
18 
     | 
    
         
             
                  @pages = Lookbook.pages
         
     | 
| 
       19 
19 
     | 
    
         
             
                  @page = @pages.find_by_path(params[:path])
         
     | 
| 
       20 
20 
     | 
    
         
             
                  if @page
         
     | 
| 
       21 
     | 
    
         
            -
                     
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                     
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
      
 21 
     | 
    
         
            +
                    if @page.errors.any?
         
     | 
| 
      
 22 
     | 
    
         
            +
                      render "lookbook/error", locals: {error: @page.errors.first}
         
     | 
| 
      
 23 
     | 
    
         
            +
                    else
         
     | 
| 
      
 24 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 25 
     | 
    
         
            +
                        @page_content = page_controller.render_page(@page)
         
     | 
| 
      
 26 
     | 
    
         
            +
                        @next_page = @pages.find_next(@page)
         
     | 
| 
      
 27 
     | 
    
         
            +
                        @previous_page = @pages.find_previous(@page)
         
     | 
| 
      
 28 
     | 
    
         
            +
                        @title = @page.title
         
     | 
| 
      
 29 
     | 
    
         
            +
                      rescue => exception
         
     | 
| 
      
 30 
     | 
    
         
            +
                        render "lookbook/error", locals: {
         
     | 
| 
      
 31 
     | 
    
         
            +
                          error: Lookbook::Error.new(exception, {
         
     | 
| 
      
 32 
     | 
    
         
            +
                            file_path: @page.full_path,
         
     | 
| 
      
 33 
     | 
    
         
            +
                            source_code: @page.content
         
     | 
| 
      
 34 
     | 
    
         
            +
                          })
         
     | 
| 
      
 35 
     | 
    
         
            +
                        }
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
       25 
38 
     | 
    
         
             
                  else
         
     | 
| 
       26 
     | 
    
         
            -
                    @title = "Not found"
         
     | 
| 
       27 
39 
     | 
    
         
             
                    render "not_found"
         
     | 
| 
       28 
40 
     | 
    
         
             
                  end
         
     | 
| 
       29 
41 
     | 
    
         
             
                end
         
     | 
| 
         @@ -1,12 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Lookbook
         
     | 
| 
       2 
2 
     | 
    
         
             
              class PreviewsController < ApplicationController
         
     | 
| 
       3 
     | 
    
         
            -
                EXCEPTIONS = [
         
     | 
| 
       4 
     | 
    
         
            -
                  ViewComponent::PreviewTemplateError,
         
     | 
| 
       5 
     | 
    
         
            -
                  ViewComponent::ComponentError,
         
     | 
| 
       6 
     | 
    
         
            -
                  ViewComponent::TemplateError,
         
     | 
| 
       7 
     | 
    
         
            -
                  ActionView::Template::Error
         
     | 
| 
       8 
     | 
    
         
            -
                ]
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
3 
     | 
    
         
             
                def self.controller_path
         
     | 
| 
       11 
4 
     | 
    
         
             
                  "lookbook/previews"
         
     | 
| 
       12 
5 
     | 
    
         
             
                end
         
     | 
| 
         @@ -17,7 +10,14 @@ module Lookbook 
     | 
|
| 
       17 
10 
     | 
    
         
             
                def preview
         
     | 
| 
       18 
11 
     | 
    
         
             
                  if @example
         
     | 
| 
       19 
12 
     | 
    
         
             
                    set_params
         
     | 
| 
       20 
     | 
    
         
            -
                     
     | 
| 
      
 13 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 14 
     | 
    
         
            +
                      render html: render_examples(examples_data)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    rescue => exception
         
     | 
| 
      
 16 
     | 
    
         
            +
                      render_in_layout "lookbook/error",
         
     | 
| 
      
 17 
     | 
    
         
            +
                        layout: "lookbook/basic",
         
     | 
| 
      
 18 
     | 
    
         
            +
                        error: prettify_error(exception),
         
     | 
| 
      
 19 
     | 
    
         
            +
                        disable_header: true
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
       21 
21 
     | 
    
         
             
                  else
         
     | 
| 
       22 
22 
     | 
    
         
             
                    render_in_layout "not_found"
         
     | 
| 
       23 
23 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -30,8 +30,8 @@ module Lookbook 
     | 
|
| 
       30 
30 
     | 
    
         
             
                      @examples = examples_data
         
     | 
| 
       31 
31 
     | 
    
         
             
                      @drawer_panels = drawer_panels.filter { |name, panel| panel[:show] }
         
     | 
| 
       32 
32 
     | 
    
         
             
                      @preview_panels = preview_panels.filter { |name, panel| panel[:show] }
         
     | 
| 
       33 
     | 
    
         
            -
                    rescue  
     | 
| 
       34 
     | 
    
         
            -
                      render_in_layout "error"
         
     | 
| 
      
 33 
     | 
    
         
            +
                    rescue => exception
         
     | 
| 
      
 34 
     | 
    
         
            +
                      render_in_layout "lookbook/error", error: prettify_error(exception)
         
     | 
| 
       35 
35 
     | 
    
         
             
                    end
         
     | 
| 
       36 
36 
     | 
    
         
             
                  else
         
     | 
| 
       37 
37 
     | 
    
         
             
                    render_in_layout "not_found"
         
     | 
| 
         @@ -152,8 +152,31 @@ module Lookbook 
     | 
|
| 
       152 
152 
     | 
    
         
             
                  @preview_controller ||= controller
         
     | 
| 
       153 
153 
     | 
    
         
             
                end
         
     | 
| 
       154 
154 
     | 
    
         | 
| 
       155 
     | 
    
         
            -
                def render_in_layout(path)
         
     | 
| 
       156 
     | 
    
         
            -
                  render  
     | 
| 
      
 155 
     | 
    
         
            +
                def render_in_layout(path, layout: nil, **locals)
         
     | 
| 
      
 156 
     | 
    
         
            +
                  render path, layout: layout.presence || (params[:lookbook_embed] ? "lookbook/basic" : "lookbook/application"), locals: locals
         
     | 
| 
      
 157 
     | 
    
         
            +
                end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                def prettify_error(exception)
         
     | 
| 
      
 160 
     | 
    
         
            +
                  error_params = if exception.is_a?(ViewComponent::PreviewTemplateError)
         
     | 
| 
      
 161 
     | 
    
         
            +
                    {
         
     | 
| 
      
 162 
     | 
    
         
            +
                      file_path: @preview&.full_path,
         
     | 
| 
      
 163 
     | 
    
         
            +
                      line_number: 0,
         
     | 
| 
      
 164 
     | 
    
         
            +
                      source_code: @example&.source
         
     | 
| 
      
 165 
     | 
    
         
            +
                    }
         
     | 
| 
      
 166 
     | 
    
         
            +
                  elsif exception.is_a?(ActionView::Template::Error) & exception.message.include?("implements a reserved method")
         
     | 
| 
      
 167 
     | 
    
         
            +
                    message_parts = exception.message.split("\n").first.split
         
     | 
| 
      
 168 
     | 
    
         
            +
                    component_class = message_parts.first.constantize
         
     | 
| 
      
 169 
     | 
    
         
            +
                    naughty_method = message_parts.last.delete("#").delete("`").delete(".")
         
     | 
| 
      
 170 
     | 
    
         
            +
                    p naughty_method
         
     | 
| 
      
 171 
     | 
    
         
            +
                    method = component_class.instance_method(naughty_method.to_sym)
         
     | 
| 
      
 172 
     | 
    
         
            +
                    if method
         
     | 
| 
      
 173 
     | 
    
         
            +
                      {
         
     | 
| 
      
 174 
     | 
    
         
            +
                        file_path: method.source_location.first,
         
     | 
| 
      
 175 
     | 
    
         
            +
                        line_number: method.source_location[1]
         
     | 
| 
      
 176 
     | 
    
         
            +
                      }
         
     | 
| 
      
 177 
     | 
    
         
            +
                    end
         
     | 
| 
      
 178 
     | 
    
         
            +
                  end
         
     | 
| 
      
 179 
     | 
    
         
            +
                  Lookbook::Error.new(exception, error_params || {})
         
     | 
| 
       157 
180 
     | 
    
         
             
                end
         
     | 
| 
       158 
181 
     | 
    
         
             
              end
         
     | 
| 
       159 
182 
     | 
    
         
             
            end
         
     | 
| 
         @@ -9,6 +9,10 @@ module Lookbook 
     | 
|
| 
       9 
9 
     | 
    
         
             
                  render_component "icon", name: name, size: size, **attrs
         
     | 
| 
       10 
10 
     | 
    
         
             
                end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
                def code(language = "ruby", **opts, &block)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  render_component "code", {language: language, **opts}, &block
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       12 
16 
     | 
    
         
             
                if Rails.version.to_f < 6.1
         
     | 
| 
       13 
17 
     | 
    
         
             
                  def class_names(*args)
         
     | 
| 
       14 
18 
     | 
    
         
             
                    tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq
         
     | 
| 
         @@ -7,10 +7,6 @@ module Lookbook 
     | 
|
| 
       7 
7 
     | 
    
         
             
                  lookbook.page_path page.lookup_path
         
     | 
| 
       8 
8 
     | 
    
         
             
                end
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                def code(language = "ruby", line_numbers: false, &block)
         
     | 
| 
       11 
     | 
    
         
            -
                  render_component "code", language: language, line_numbers: line_numbers, &block
         
     | 
| 
       12 
     | 
    
         
            -
                end
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
10 
     | 
    
         
             
                def embed(*args, params: {}, type: :preview, **opts)
         
     | 
| 
       15 
11 
     | 
    
         
             
                  return unless args.any?
         
     | 
| 
       16 
12 
     | 
    
         | 
| 
         @@ -1,13 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            <%
         
     | 
| 
       2 
     | 
    
         
            -
            line_numbers ||= false
         
     | 
| 
       3 
2 
     | 
    
         
             
            language ||= "html"
         
     | 
| 
       4 
3 
     | 
    
         
             
            wrap ||= nil;
         
     | 
| 
       5 
4 
     | 
    
         
             
            %>
         
     | 
| 
       6 
5 
     | 
    
         
             
            <% code ||= capture do %><%= yield %><% end %>
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
            <% output = highlight(code, language, {
         
     | 
| 
      
 7 
     | 
    
         
            +
              strip: defined?(strip) ? strip : true,
         
     | 
| 
      
 8 
     | 
    
         
            +
              line_numbers: line_numbers ||= false,
         
     | 
| 
      
 9 
     | 
    
         
            +
              highlight_lines: highlight_lines ||= [],
         
     | 
| 
      
 10 
     | 
    
         
            +
              start_line: start_line ||= 0
         
     | 
| 
      
 11 
     | 
    
         
            +
            }) %>
         
     | 
| 
      
 12 
     | 
    
         
            +
            <div class="code not-prose <%= "numbered" if line_numbers %> <%= classes ||= "" %> <%= "focussed" if highlight_lines.any? %>"
         
     | 
| 
       8 
13 
     | 
    
         
             
              x-data="code"
         
     | 
| 
       9 
14 
     | 
    
         
             
              :class="{'wrapped': wrap}"
         
     | 
| 
       10 
     | 
    
         
            -
              <% if wrap.present? %>x-effect="wrap = <%= wrap %>"<% end  
     | 
| 
       11 
     | 
    
         
            -
            >
         
     | 
| 
       12 
     | 
    
         
            -
              <pre><code class="highlight"><%= highlight(code.strip, language, line_numbers: line_numbers) %></code></pre>
         
     | 
| 
      
 15 
     | 
    
         
            +
              <% if wrap.present? %>x-effect="wrap = <%= wrap %>"<% end %>>
         
     | 
| 
      
 16 
     | 
    
         
            +
              <pre><code class="highlight"><%= output %></code></pre>
         
     | 
| 
       13 
17 
     | 
    
         
             
            </div>
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <div class="bg-red-50 w-full overflow-auto h-full">
         
     | 
| 
      
 2 
     | 
    
         
            +
              <ul class="text-sm divide-y divide-red-200">
         
     | 
| 
      
 3 
     | 
    
         
            +
                <% errors.each do |error| %>
         
     | 
| 
      
 4 
     | 
    
         
            +
                  <% error = error.is_a?(Lookbook::Error) ? error : Lookbook::Error.new(error) %>
         
     | 
| 
      
 5 
     | 
    
         
            +
                  <li class="px-4 py-3">
         
     | 
| 
      
 6 
     | 
    
         
            +
                    <h4 class="break-all leading-tight">
         
     | 
| 
      
 7 
     | 
    
         
            +
                      <%= error.file_name %><%= ":#{error.line_number}" if error.line_number %>
         
     | 
| 
      
 8 
     | 
    
         
            +
                    </h4>
         
     | 
| 
      
 9 
     | 
    
         
            +
                    <pre class="text-red-800 text-xs mt-2 whitespace-pre-wrap opacity-80 font-mono"><%= error.message %></pre>
         
     | 
| 
      
 10 
     | 
    
         
            +
                  </li>
         
     | 
| 
      
 11 
     | 
    
         
            +
                <% end %>
         
     | 
| 
      
 12 
     | 
    
         
            +
              </ul>
         
     | 
| 
      
 13 
     | 
    
         
            +
            </div>
         
     | 
| 
         @@ -22,7 +22,7 @@ 
     | 
|
| 
       22 
22 
     | 
    
         
             
                </div>
         
     | 
| 
       23 
23 
     | 
    
         
             
                <div class="flex items-stretch h-full ml-auto space-x-3">
         
     | 
| 
       24 
24 
     | 
    
         
             
                  <div
         
     | 
| 
       25 
     | 
    
         
            -
                    class="flex items-center text-xs font- 
     | 
| 
      
 25 
     | 
    
         
            +
                    class="flex items-center text-xs font-mono text-gray-700 space-x-1 opacity-50 hover:opacity-100 transition"
         
     | 
| 
       26 
26 
     | 
    
         
             
                    :class="{'opacity-100': $store.inspector.preview.resizing}"
         
     | 
| 
       27 
27 
     | 
    
         
             
                    x-show="isActivePreviewPanel('preview')">
         
     | 
| 
       28 
28 
     | 
    
         
             
                    <span x-text="`${preview.width}px`"></span>
         
     | 
| 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            <div
         
     | 
| 
       2 
2 
     | 
    
         
             
              x-data="sidebar"
         
     | 
| 
       3 
3 
     | 
    
         
             
              @page:morphed.window="setActiveNavItem"
         
     | 
| 
       4 
     | 
    
         
            -
              class="h-full bg-gray-100 overflow-hidden flex flex-col"
         
     | 
| 
      
 4 
     | 
    
         
            +
              class="h-full bg-gray-100 overflow-hidden flex flex-col relative"
         
     | 
| 
       5 
5 
     | 
    
         
             
              x-show="$store.sidebar.open"
         
     | 
| 
       6 
6 
     | 
    
         
             
              x-cloak>
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
         @@ -14,8 +14,8 @@ 
     | 
|
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
              <% if feature_enabled?(:pages) && Lookbook.pages.any? %>
         
     | 
| 
       16 
16 
     | 
    
         
             
                <div
         
     | 
| 
       17 
     | 
    
         
            -
                  class="grid overflow-hidden"
         
     | 
| 
       18 
     | 
    
         
            -
                  :style="`grid-template-rows: ${$store.sidebar.panelSplits[0] || 1}fr 1px ${$store.sidebar.panelSplits[1] || 1}fr 
     | 
| 
      
 17 
     | 
    
         
            +
                  class="grid overflow-hidden flex-grow"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  :style="`grid-template-rows: ${$store.sidebar.panelSplits[0] || 1}fr 1px ${$store.sidebar.panelSplits[1] || 1}fr;`"
         
     | 
| 
       19 
19 
     | 
    
         
             
                  x-ref="sidebarPanels">
         
     | 
| 
       20 
20 
     | 
    
         
             
                  <div class="flex flex-col overflow-hidden">
         
     | 
| 
       21 
21 
     | 
    
         
             
                    <div class="flex items-center flex-none border-b border-gray-300 h-10 bg-white relative px-4">   
         
     | 
| 
         @@ -52,4 +52,18 @@ 
     | 
|
| 
       52 
52 
     | 
    
         
             
                </div>
         
     | 
| 
       53 
53 
     | 
    
         
             
              <% end %>
         
     | 
| 
       54 
54 
     | 
    
         | 
| 
      
 55 
     | 
    
         
            +
              <% if Lookbook::Preview.errors.any? %>
         
     | 
| 
      
 56 
     | 
    
         
            +
                <div class="flex-none" x-ref="preview-errors" id="preview-errors">
         
     | 
| 
      
 57 
     | 
    
         
            +
                  <div class="flex items-center border-b border-t border-gray-300 h-10 bg-white px-4">   
         
     | 
| 
      
 58 
     | 
    
         
            +
                    <h2 class="flex items-center flex-none">
         
     | 
| 
      
 59 
     | 
    
         
            +
                      <%= icon "alert-triangle", size: 4, class: "text-red-700" %>
         
     | 
| 
      
 60 
     | 
    
         
            +
                      <span class="ml-2">Preview load errors</span>
         
     | 
| 
      
 61 
     | 
    
         
            +
                    </h2>
         
     | 
| 
      
 62 
     | 
    
         
            +
                  </div>
         
     | 
| 
      
 63 
     | 
    
         
            +
                  <div class="h-full max-h-[300px] overflow-hidden">
         
     | 
| 
      
 64 
     | 
    
         
            +
                    <%= component "errors", errors: Lookbook::Preview.errors %>
         
     | 
| 
      
 65 
     | 
    
         
            +
                  </div>
         
     | 
| 
      
 66 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 67 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
       55 
69 
     | 
    
         
             
            </div>
         
     | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <%
         
     | 
| 
      
 2 
     | 
    
         
            +
            error = error.is_a?(Lookbook::Error) ? error : Lookbook::Error.new(error)
         
     | 
| 
      
 3 
     | 
    
         
            +
            @title = error.title
         
     | 
| 
      
 4 
     | 
    
         
            +
            disable_header ||= false
         
     | 
| 
      
 5 
     | 
    
         
            +
            %>
         
     | 
| 
      
 6 
     | 
    
         
            +
            <%= component "header" unless disable_header %>
         
     | 
| 
      
 7 
     | 
    
         
            +
            <div class="h-full w-full bg-red-50 flex flex-col border-red-300" id="error-<%= Time.now %>">
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              <header class="mx-8  pt-8 mb-8 flex-none">
         
     | 
| 
      
 10 
     | 
    
         
            +
                <h2 class="text-xl font-bold text-red-700"><%= error.title %></h2>
         
     | 
| 
      
 11 
     | 
    
         
            +
              </header>
         
     | 
| 
      
 12 
     | 
    
         
            +
              
         
     | 
| 
      
 13 
     | 
    
         
            +
              <div class="flex-none px-8 py-6 mb-8 border-t border-b border-red-200 bg-red-100 text-base font-mono leading-relaxed">
         
     | 
| 
      
 14 
     | 
    
         
            +
                <pre class="whitespace-pre-wrap font-sans leading-tight text-red-900"><%= error.message %></pre>
         
     | 
| 
      
 15 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              <% if error.file_name %>
         
     | 
| 
      
 18 
     | 
    
         
            +
                <div class="text-gray-800 text-sm mx-8 mb-2 font-mono flex-none <%= "pl-2" if error.source_code %>">
         
     | 
| 
      
 19 
     | 
    
         
            +
                  <span><%= error.file_name %></span>
         
     | 
| 
      
 20 
     | 
    
         
            +
                  <% if error.line_number %>
         
     | 
| 
      
 21 
     | 
    
         
            +
                    <span>[line <strong><%= error.line_number %></strong>]</span>
         
     | 
| 
      
 22 
     | 
    
         
            +
                  <% end %>
         
     | 
| 
      
 23 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 24 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              <% if error.source_code %>
         
     | 
| 
      
 27 
     | 
    
         
            +
              <div class="prose max-w-full mx-8">
         
     | 
| 
      
 28 
     | 
    
         
            +
                  <%= code error.file_lang, highlight_lines: [error.source_code[:highlighted_line]],
         
     | 
| 
      
 29 
     | 
    
         
            +
                    line_numbers: true,
         
     | 
| 
      
 30 
     | 
    
         
            +
                    start_line: error.source_code[:start_line],
         
     | 
| 
      
 31 
     | 
    
         
            +
                    strip: false,
         
     | 
| 
      
 32 
     | 
    
         
            +
                    class: "py-4 !max-w-full !border-red-200 !mt-1" do %><%= h(error.source_code[:code]) %><% end %>
         
     | 
| 
      
 33 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 34 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              <h3 class="font-bold mb-4 px-8 mt-8 flex-none">Full stack trace</h3>
         
     | 
| 
      
 37 
     | 
    
         
            +
              <div class="text-xs font-mono flex-grow h-full overflow-hidden">
         
     | 
| 
      
 38 
     | 
    
         
            +
                <div class="h-full overflow-auto px-8 pb-10 text-gray-400 leading-relaxed">
         
     | 
| 
      
 39 
     | 
    
         
            +
                  <% error.backtrace.each do |line| %>
         
     | 
| 
      
 40 
     | 
    
         
            +
                    <div class="hover:text-gray-900 transition-colors duration-100">
         
     | 
| 
      
 41 
     | 
    
         
            +
                      <%= line %>
         
     | 
| 
      
 42 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 43 
     | 
    
         
            +
                  <% end %>
         
     | 
| 
      
 44 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 45 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 46 
     | 
    
         
            +
            </div>
         
     | 
| 
         @@ -5,7 +5,8 @@ module Lookbook 
     | 
|
| 
       5 
5 
     | 
    
         
             
              module CodeFormatter
         
     | 
| 
       6 
6 
     | 
    
         
             
                class << self
         
     | 
| 
       7 
7 
     | 
    
         
             
                  def highlight(source, language, opts = {})
         
     | 
| 
       8 
     | 
    
         
            -
                    source&. 
     | 
| 
      
 8 
     | 
    
         
            +
                    source&.strip! unless opts[:strip] == false
         
     | 
| 
      
 9 
     | 
    
         
            +
                    source&.gsub!(">", ">")&.gsub!("<", "<")
         
     | 
| 
       9 
10 
     | 
    
         
             
                    language ||= "ruby"
         
     | 
| 
       10 
11 
     | 
    
         
             
                    formatter = Formatter.new(opts)
         
     | 
| 
       11 
12 
     | 
    
         
             
                    lexer = Rouge::Lexer.find(language.to_s) || Rouge::Lexer.find("plaintext")
         
     | 
| 
         @@ -23,12 +24,14 @@ module Lookbook 
     | 
|
| 
       23 
24 
     | 
    
         
             
              class Formatter < Rouge::Formatters::HTML
         
     | 
| 
       24 
25 
     | 
    
         
             
                def initialize(opts = {})
         
     | 
| 
       25 
26 
     | 
    
         
             
                  @opts = opts
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @highlight_lines = opts[:highlight_lines].to_a || []
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @start_line = opts[:start_line] || 0
         
     | 
| 
       26 
29 
     | 
    
         
             
                end
         
     | 
| 
       27 
30 
     | 
    
         | 
| 
       28 
31 
     | 
    
         
             
                def stream(tokens, &block)
         
     | 
| 
       29 
32 
     | 
    
         
             
                  token_lines(tokens).each_with_index do |line_tokens, i|
         
     | 
| 
       30 
     | 
    
         
            -
                    yield "<div class='line'>"
         
     | 
| 
       31 
     | 
    
         
            -
                    yield "<span class='line-number'>#{i}</span>" if @opts[:line_numbers]
         
     | 
| 
      
 33 
     | 
    
         
            +
                    yield "<div class='line #{"highlighted-line" if @highlight_lines.include?(i + 1)}'>"
         
     | 
| 
      
 34 
     | 
    
         
            +
                    yield "<span class='line-number'>#{@start_line + i}</span>" if @opts[:line_numbers]
         
     | 
| 
       32 
35 
     | 
    
         
             
                    yield "<span class='line-content'>"
         
     | 
| 
       33 
36 
     | 
    
         
             
                    line_tokens.each do |token, value|
         
     | 
| 
       34 
37 
     | 
    
         
             
                      yield span(token, value)
         
     | 
    
        data/lib/lookbook/engine.rb
    CHANGED
    
    | 
         @@ -82,22 +82,21 @@ module Lookbook 
     | 
|
| 
       82 
82 
     | 
    
         
             
                  end
         
     | 
| 
       83 
83 
     | 
    
         
             
                end
         
     | 
| 
       84 
84 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
                initializer "lookbook.helpers" do
         
     | 
| 
       86 
     | 
    
         
            -
                  config.action_controller.include_all_helpers = false
         
     | 
| 
       87 
     | 
    
         
            -
                end
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
85 
     | 
    
         
             
                config.after_initialize do
         
     | 
| 
       90 
     | 
    
         
            -
                  Array(config.view_component.preview_paths).each do |preview_path|
         
     | 
| 
       91 
     | 
    
         
            -
                    Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
         
     | 
| 
       92 
     | 
    
         
            -
                  end
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
86 
     | 
    
         
             
                  @preview_listener = Listen.to(*config.lookbook.listen_paths, only: /\.(rb|html.*)$/) do |modified, added, removed|
         
     | 
| 
       95 
     | 
    
         
            -
                     
     | 
| 
      
 87 
     | 
    
         
            +
                    if Lookbook::Preview.errors.any?
         
     | 
| 
      
 88 
     | 
    
         
            +
                      Lookbook::Preview.reload
         
     | 
| 
      
 89 
     | 
    
         
            +
                    end
         
     | 
| 
      
 90 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 91 
     | 
    
         
            +
                      parser.parse
         
     | 
| 
      
 92 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
       96 
94 
     | 
    
         
             
                    if Lookbook::Engine.websocket
         
     | 
| 
       97 
     | 
    
         
            -
                      if  
     | 
| 
      
 95 
     | 
    
         
            +
                      if modified.any? || removed.any? || added.none?
         
     | 
| 
       98 
96 
     | 
    
         
             
                        Lookbook::Engine.websocket.broadcast("reload", {
         
     | 
| 
       99 
97 
     | 
    
         
             
                          modified: modified,
         
     | 
| 
       100 
     | 
    
         
            -
                          removed: removed
         
     | 
| 
      
 98 
     | 
    
         
            +
                          removed: removed,
         
     | 
| 
      
 99 
     | 
    
         
            +
                          added: added
         
     | 
| 
       101 
100 
     | 
    
         
             
                        })
         
     | 
| 
       102 
101 
     | 
    
         
             
                      end
         
     | 
| 
       103 
102 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -0,0 +1,121 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Lookbook
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Error < StandardError
         
     | 
| 
      
 3 
     | 
    
         
            +
                delegate :full_message, :backtrace, :to_s, to: :target
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                LINES_AROUND = 3
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(original = nil, title: nil, message: nil, file_path: nil, file_name: nil, line_number: nil, source_code: nil)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @original = original
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @title = title
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @message = message
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @file_path = file_path
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @file_name = file_name
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @line_number = line_number
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @source_code = source_code
         
     | 
| 
      
 15 
     | 
    
         
            +
                  super()
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def source_code
         
     | 
| 
      
 19 
     | 
    
         
            +
                  lines = source_code_lines
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  if lines.present? && line_number.is_a?(Integer)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    start_line = source_code_start_line(lines)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end_line = source_code_end_line(lines)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    highlighted_line = source_code_highlighted_line(lines)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    line_count = end_line - start_line
         
     | 
| 
      
 27 
     | 
    
         
            +
                    relevant_lines = lines.slice(start_line - 1, line_count + 1)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    if relevant_lines.present?
         
     | 
| 
      
 29 
     | 
    
         
            +
                      empty_start_lines = 0
         
     | 
| 
      
 30 
     | 
    
         
            +
                      relevant_lines.each do |line|
         
     | 
| 
      
 31 
     | 
    
         
            +
                        break unless line.strip.empty?
         
     | 
| 
      
 32 
     | 
    
         
            +
                        empty_start_lines += 1
         
     | 
| 
      
 33 
     | 
    
         
            +
                      end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                      {
         
     | 
| 
      
 36 
     | 
    
         
            +
                        code: relevant_lines.join("\n").lstrip,
         
     | 
| 
      
 37 
     | 
    
         
            +
                        start_line: start_line - empty_start_lines,
         
     | 
| 
      
 38 
     | 
    
         
            +
                        end_line: end_line - empty_start_lines,
         
     | 
| 
      
 39 
     | 
    
         
            +
                        highlighted_line: highlighted_line - empty_start_lines
         
     | 
| 
      
 40 
     | 
    
         
            +
                      }
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def source_code_lines
         
     | 
| 
      
 47 
     | 
    
         
            +
                  if file_path || @source_code
         
     | 
| 
      
 48 
     | 
    
         
            +
                    if @source_code
         
     | 
| 
      
 49 
     | 
    
         
            +
                      @source_code.split("\n")
         
     | 
| 
      
 50 
     | 
    
         
            +
                    else
         
     | 
| 
      
 51 
     | 
    
         
            +
                      full_path = Rails.root.join(file_path)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      File.read(full_path).split("\n") if File.exist? full_path
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def file_lang
         
     | 
| 
      
 58 
     | 
    
         
            +
                  lang = Lookbook::Lang.guess(file_path)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  lang.present? ? lang[:name] : "plaintext"
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def title
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @title || target.class.to_s
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                def message
         
     | 
| 
      
 67 
     | 
    
         
            +
                  (@message || target.message).gsub("(<unknown>):", "").strip.upcase_first
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def file_name
         
     | 
| 
      
 71 
     | 
    
         
            +
                  if @file_name == false
         
     | 
| 
      
 72 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 73 
     | 
    
         
            +
                  else
         
     | 
| 
      
 74 
     | 
    
         
            +
                    @file_name || file_path
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def file_path
         
     | 
| 
      
 79 
     | 
    
         
            +
                  path = if @file_path.nil?
         
     | 
| 
      
 80 
     | 
    
         
            +
                    parsed_backtrace[0][0] if parsed_backtrace.any?
         
     | 
| 
      
 81 
     | 
    
         
            +
                  else
         
     | 
| 
      
 82 
     | 
    
         
            +
                    @file_path.presence || nil
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                  path.nil? ? nil : path.to_s.delete_prefix("#{Rails.root}/")
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def line_number
         
     | 
| 
      
 88 
     | 
    
         
            +
                  number = if @line_number.nil?
         
     | 
| 
      
 89 
     | 
    
         
            +
                    parsed_backtrace[0][1] if parsed_backtrace.any?
         
     | 
| 
      
 90 
     | 
    
         
            +
                  else
         
     | 
| 
      
 91 
     | 
    
         
            +
                    @line_number.presence || nil
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                  number.present? ? number.to_i : number
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                def parsed_backtrace
         
     | 
| 
      
 97 
     | 
    
         
            +
                  backtrace.map do |x|
         
     | 
| 
      
 98 
     | 
    
         
            +
                    x =~ /^(.+?):(\d+)(|:in `(.+)')$/
         
     | 
| 
      
 99 
     | 
    
         
            +
                    [$1, $2, $4]
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                protected
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                def target
         
     | 
| 
      
 106 
     | 
    
         
            +
                  @original.presence || self
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                def source_code_start_line(lines)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  [(line_number - LINES_AROUND), 1].max unless line_number.nil?
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                def source_code_end_line(lines)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  [line_number + LINES_AROUND, lines&.size || Infinity].min
         
     | 
| 
      
 115 
     | 
    
         
            +
                end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                def source_code_highlighted_line(lines)
         
     | 
| 
      
 118 
     | 
    
         
            +
                  [line_number - source_code_start_line(lines) + 1, 1].max unless line_number.nil?
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
              end
         
     | 
| 
      
 121 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/lookbook/page.rb
    CHANGED
    
    | 
         @@ -15,9 +15,13 @@ module Lookbook 
     | 
|
| 
       15 
15 
     | 
    
         
             
                  :data
         
     | 
| 
       16 
16 
     | 
    
         
             
                ]
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
                attr_reader :errors
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       18 
20 
     | 
    
         
             
                def initialize(path, base_path)
         
     | 
| 
       19 
21 
     | 
    
         
             
                  @pathname = Pathname.new path
         
     | 
| 
       20 
22 
     | 
    
         
             
                  @base_path = base_path
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @options = nil
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @errors = []
         
     | 
| 
       21 
25 
     | 
    
         
             
                end
         
     | 
| 
       22 
26 
     | 
    
         | 
| 
       23 
27 
     | 
    
         
             
                def path
         
     | 
| 
         @@ -58,7 +62,7 @@ module Lookbook 
     | 
|
| 
       58 
62 
     | 
    
         
             
                end
         
     | 
| 
       59 
63 
     | 
    
         | 
| 
       60 
64 
     | 
    
         
             
                def content
         
     | 
| 
       61 
     | 
    
         
            -
                  @content ||= strip_frontmatter(file_contents)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @content ||= strip_frontmatter(file_contents).strip
         
     | 
| 
       62 
66 
     | 
    
         
             
                end
         
     | 
| 
       63 
67 
     | 
    
         | 
| 
       64 
68 
     | 
    
         
             
                def matchers
         
     | 
| 
         @@ -97,18 +101,28 @@ module Lookbook 
     | 
|
| 
       97 
101 
     | 
    
         | 
| 
       98 
102 
     | 
    
         
             
                def options
         
     | 
| 
       99 
103 
     | 
    
         
             
                  return @options if @options
         
     | 
| 
       100 
     | 
    
         
            -
                   
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
                   
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
                   
     | 
| 
       111 
     | 
    
         
            -
                  @options  
     | 
| 
      
 104 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 105 
     | 
    
         
            +
                    frontmatter = (get_frontmatter(file_contents) || {}).deep_symbolize_keys
         
     | 
| 
      
 106 
     | 
    
         
            +
                  rescue => exception
         
     | 
| 
      
 107 
     | 
    
         
            +
                    frontmatter = {}
         
     | 
| 
      
 108 
     | 
    
         
            +
                    line_number_match = exception.message.match(/.*line\s(\d+)/)
         
     | 
| 
      
 109 
     | 
    
         
            +
                    @errors.push(Lookbook::Error.new(exception, {
         
     | 
| 
      
 110 
     | 
    
         
            +
                      title: "YAML frontmatter parsing error",
         
     | 
| 
      
 111 
     | 
    
         
            +
                      file_path: @pathname.to_s,
         
     | 
| 
      
 112 
     | 
    
         
            +
                      line_number: line_number_match ? line_number_match[1] : false
         
     | 
| 
      
 113 
     | 
    
         
            +
                    }))
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @options = Lookbook.config.page_options.deep_merge(frontmatter).with_indifferent_access
         
     | 
| 
      
 116 
     | 
    
         
            +
                  @options[:id] = @options[:id] ? generate_id(@options[:id]) : generate_id(lookup_path)
         
     | 
| 
      
 117 
     | 
    
         
            +
                  @options[:label] ||= name.titleize
         
     | 
| 
      
 118 
     | 
    
         
            +
                  @options[:title] ||= @options[:label]
         
     | 
| 
      
 119 
     | 
    
         
            +
                  @options[:hidden] ||= false
         
     | 
| 
      
 120 
     | 
    
         
            +
                  @options[:landing] ||= false
         
     | 
| 
      
 121 
     | 
    
         
            +
                  @options[:position] = @options[:position] ? @options[:position].to_i : get_position_prefix(path_name)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  @options[:markdown] ||= markdown_file?
         
     | 
| 
      
 123 
     | 
    
         
            +
                  @options[:header] = true unless @options.key? :header
         
     | 
| 
      
 124 
     | 
    
         
            +
                  @options[:footer] = true unless @options.key? :footer
         
     | 
| 
      
 125 
     | 
    
         
            +
                  @options
         
     | 
| 
       112 
126 
     | 
    
         
             
                end
         
     | 
| 
       113 
127 
     | 
    
         | 
| 
       114 
128 
     | 
    
         
             
                def path_name
         
     | 
    
        data/lib/lookbook/preview.rb
    CHANGED
    
    | 
         @@ -34,7 +34,7 @@ module Lookbook 
     | 
|
| 
       34 
34 
     | 
    
         
             
                  return @examples if @examples.present?
         
     | 
| 
       35 
35 
     | 
    
         
             
                  public_methods = @preview.public_instance_methods(false)
         
     | 
| 
       36 
36 
     | 
    
         
             
                  public_method_objects = @preview_inspector&.methods&.filter { |m| public_methods.include?(m.name) }
         
     | 
| 
       37 
     | 
    
         
            -
                  examples = public_method_objects 
     | 
| 
      
 37 
     | 
    
         
            +
                  examples = (public_method_objects || []).map { |m| PreviewExample.new(m.name.to_s, self) }
         
     | 
| 
       38 
38 
     | 
    
         
             
                  sorted = Lookbook.config.sort_examples ? examples.sort_by(&:label) : examples
         
     | 
| 
       39 
39 
     | 
    
         
             
                  @examples = []
         
     | 
| 
       40 
40 
     | 
    
         
             
                  if @preview_inspector&.groups&.any?
         
     | 
| 
         @@ -49,7 +49,7 @@ module Lookbook 
     | 
|
| 
       49 
49 
     | 
    
         
             
                  else
         
     | 
| 
       50 
50 
     | 
    
         
             
                    @examples = sorted
         
     | 
| 
       51 
51 
     | 
    
         
             
                  end
         
     | 
| 
       52 
     | 
    
         
            -
                  @examples
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @examples = @examples.compact
         
     | 
| 
       53 
53 
     | 
    
         
             
                end
         
     | 
| 
       54 
54 
     | 
    
         | 
| 
       55 
55 
     | 
    
         
             
                def default_example
         
     | 
| 
         @@ -68,7 +68,7 @@ module Lookbook 
     | 
|
| 
       68 
68 
     | 
    
         
             
                end
         
     | 
| 
       69 
69 
     | 
    
         | 
| 
       70 
70 
     | 
    
         
             
                def preview_paths
         
     | 
| 
       71 
     | 
    
         
            -
                  ViewComponent:: 
     | 
| 
      
 71 
     | 
    
         
            +
                  ViewComponent::Base.preview_paths
         
     | 
| 
       72 
72 
     | 
    
         
             
                end
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
       74 
74 
     | 
    
         
             
                def parent_collections_names
         
     | 
| 
         @@ -88,19 +88,78 @@ module Lookbook 
     | 
|
| 
       88 
88 
     | 
    
         
             
                end
         
     | 
| 
       89 
89 
     | 
    
         | 
| 
       90 
90 
     | 
    
         
             
                class << self
         
     | 
| 
      
 91 
     | 
    
         
            +
                  def find(path)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    all.find { |p| p.lookup_path == path }
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  def exists?(path)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    !!find(path)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
       91 
99 
     | 
    
         
             
                  def all
         
     | 
| 
       92 
     | 
    
         
            -
                    previews =  
     | 
| 
      
 100 
     | 
    
         
            +
                    previews = load_previews.map do |p|
         
     | 
| 
      
 101 
     | 
    
         
            +
                      new(p)
         
     | 
| 
      
 102 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 103 
     | 
    
         
            +
                      Rails.logger.error "[lookbook] error instantiating preview\n#{exception.full_message}"
         
     | 
| 
      
 104 
     | 
    
         
            +
                    end
         
     | 
| 
       93 
105 
     | 
    
         | 
| 
       94 
     | 
    
         
            -
                    sorted_previews = previews.sort_by { |preview| [preview.position, preview.label] }
         
     | 
| 
      
 106 
     | 
    
         
            +
                    sorted_previews = previews.compact.sort_by { |preview| [preview.position, preview.label] }
         
     | 
| 
       95 
107 
     | 
    
         
             
                    PreviewCollection.new(sorted_previews)
         
     | 
| 
       96 
108 
     | 
    
         
             
                  end
         
     | 
| 
       97 
109 
     | 
    
         | 
| 
       98 
     | 
    
         
            -
                  def  
     | 
| 
       99 
     | 
    
         
            -
                     
     | 
| 
      
 110 
     | 
    
         
            +
                  def errors
         
     | 
| 
      
 111 
     | 
    
         
            +
                    @errors || []
         
     | 
| 
       100 
112 
     | 
    
         
             
                  end
         
     | 
| 
       101 
113 
     | 
    
         | 
| 
       102 
     | 
    
         
            -
                  def  
     | 
| 
       103 
     | 
    
         
            -
                     
     | 
| 
      
 114 
     | 
    
         
            +
                  def reload
         
     | 
| 
      
 115 
     | 
    
         
            +
                    load_previews
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  def reset_files_data
         
     | 
| 
      
 121 
     | 
    
         
            +
                    @loaded_files = []
         
     | 
| 
      
 122 
     | 
    
         
            +
                    @errors = []
         
     | 
| 
      
 123 
     | 
    
         
            +
                  end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                  def load_previews
         
     | 
| 
      
 126 
     | 
    
         
            +
                    reset_files_data if @loaded_files.nil?
         
     | 
| 
      
 127 
     | 
    
         
            +
                    require_preview_files if @errors.any?
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                    preview_classes = ViewComponent::Preview.descendants
         
     | 
| 
      
 130 
     | 
    
         
            +
                    if preview_files.size > preview_classes.size
         
     | 
| 
      
 131 
     | 
    
         
            +
                      require_preview_files
         
     | 
| 
      
 132 
     | 
    
         
            +
                    end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                    ViewComponent::Preview.descendants.filter { |klass| @loaded_files.include? "#{klass.name.underscore}.rb" }
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                  def require_preview_files
         
     | 
| 
      
 138 
     | 
    
         
            +
                    reset_files_data
         
     | 
| 
      
 139 
     | 
    
         
            +
                    preview_files.each do |file|
         
     | 
| 
      
 140 
     | 
    
         
            +
                      require_dependency(file[:path])
         
     | 
| 
      
 141 
     | 
    
         
            +
                      @loaded_files.push(file[:rel_path])
         
     | 
| 
      
 142 
     | 
    
         
            +
                    rescue => exception
         
     | 
| 
      
 143 
     | 
    
         
            +
                      Rails.logger.error "[lookbook] preview error\n#{exception.full_message}\n"
         
     | 
| 
      
 144 
     | 
    
         
            +
                      @errors.push(Lookbook::Error.new(exception, {
         
     | 
| 
      
 145 
     | 
    
         
            +
                        title: "Preview #{exception.class}",
         
     | 
| 
      
 146 
     | 
    
         
            +
                        file_name: file[:rel_path],
         
     | 
| 
      
 147 
     | 
    
         
            +
                        file_path: file[:path]
         
     | 
| 
      
 148 
     | 
    
         
            +
                      }))
         
     | 
| 
      
 149 
     | 
    
         
            +
                    end
         
     | 
| 
      
 150 
     | 
    
         
            +
                  end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                  def preview_files
         
     | 
| 
      
 153 
     | 
    
         
            +
                    files = Array(Lookbook.config.preview_paths).map do |preview_path|
         
     | 
| 
      
 154 
     | 
    
         
            +
                      Dir["#{preview_path}/**/*_preview.rb"].map do |path|
         
     | 
| 
      
 155 
     | 
    
         
            +
                        {
         
     | 
| 
      
 156 
     | 
    
         
            +
                          path: path,
         
     | 
| 
      
 157 
     | 
    
         
            +
                          base_path: preview_path,
         
     | 
| 
      
 158 
     | 
    
         
            +
                          rel_path: Pathname(path).relative_path_from(preview_path).to_s
         
     | 
| 
      
 159 
     | 
    
         
            +
                        }
         
     | 
| 
      
 160 
     | 
    
         
            +
                      end
         
     | 
| 
      
 161 
     | 
    
         
            +
                    end
         
     | 
| 
      
 162 
     | 
    
         
            +
                    files.flatten
         
     | 
| 
       104 
163 
     | 
    
         
             
                  end
         
     | 
| 
       105 
164 
     | 
    
         
             
                end
         
     | 
| 
       106 
165 
     | 
    
         |