lookbook 0.7.2.beta.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98a56fb4554ef243a33b570a86191d836574e2557bc9800fc0ce948b5477aeb6
4
- data.tar.gz: b8d46feaf068e9cdfe79cb7677d3c6f8cc90739e699b8909fce82fea658115e0
3
+ metadata.gz: 7c26bf5a4901bcb0fd09b8742ae3e9c2df8f8c337c3f312152cb31dc6a74aa23
4
+ data.tar.gz: e1e8a016b29cf87357c3b291c99eaaf8d1e21d1a4acb552bab7777699579f265
5
5
  SHA512:
6
- metadata.gz: 3f7ce11e780bdd6c3d77a67a393e35e1fb9d2a3fd0eaf10f3748dcd4a9dbd83f4130f5a6877887518c02f95f1885c468ccbe06db3c6b02052a02df401a6aadd1
7
- data.tar.gz: f8f0bfbf93acad2e103d7b6a84dff701597579ed0fab778f1ee74276f5e23d4f260472dc6e64122a9127ee3be3c9fb44ee8abf56dd94fa7b6707e1a3de4089b7
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 text-sm rounded-md py-4 overflow-auto max-w-3xl w-full mx-auto;
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 {
@@ -27,7 +27,12 @@ module Lookbook
27
27
  @previous_page = @pages.find_previous(@page)
28
28
  @title = @page.title
29
29
  rescue => exception
30
- render "lookbook/error", locals: {error: 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
+ }
31
36
  end
32
37
  end
33
38
  else
@@ -13,7 +13,10 @@ module Lookbook
13
13
  begin
14
14
  render html: render_examples(examples_data)
15
15
  rescue => exception
16
- render_in_layout "lookbook/error", layout: "lookbook/basic", error: exception, disable_header: true
16
+ render_in_layout "lookbook/error",
17
+ layout: "lookbook/basic",
18
+ error: prettify_error(exception),
19
+ disable_header: true
17
20
  end
18
21
  else
19
22
  render_in_layout "not_found"
@@ -28,7 +31,7 @@ module Lookbook
28
31
  @drawer_panels = drawer_panels.filter { |name, panel| panel[:show] }
29
32
  @preview_panels = preview_panels.filter { |name, panel| panel[:show] }
30
33
  rescue => exception
31
- render_in_layout "lookbook/error", error: exception
34
+ render_in_layout "lookbook/error", error: prettify_error(exception)
32
35
  end
33
36
  else
34
37
  render_in_layout "not_found"
@@ -152,5 +155,28 @@ module Lookbook
152
155
  def render_in_layout(path, layout: nil, **locals)
153
156
  render path, layout: layout.presence || (params[:lookbook_embed] ? "lookbook/basic" : "lookbook/application"), locals: locals
154
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 || {})
180
+ end
155
181
  end
156
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
- <div class="code not-prose <%= "numbered" if line_numbers %> <%= classes ||= "" %>"
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>
@@ -4,25 +4,40 @@ error = error.is_a?(Lookbook::Error) ? error : Lookbook::Error.new(error)
4
4
  disable_header ||= false
5
5
  %>
6
6
  <%= component "header" unless disable_header %>
7
- <div class="h-full w-full bg-red-50 flex flex-col border-red-300">
8
- <header class="flex-none px-8 py-8">
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">
9
10
  <h2 class="text-xl font-bold text-red-700"><%= error.title %></h2>
10
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>
11
16
 
12
- <div class="flex-none px-8 py-6 border-t border-b border-red-200 bg-red-100 text-base font-mono leading-relaxed">
13
- <% if error.file_name %>
14
- <div class="mb-2 text-gray-500">
15
- <%= error.file_name %><%= ":#{error.line_number}" if error.line_number %>
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 %>
16
23
  </div>
17
- <% end %>
18
- <pre class="whitespace-pre-wrap"><%= error.message %></pre>
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 %>
19
33
  </div>
34
+ <% end %>
20
35
 
21
- <h3 class="font-bold mt-6 mb-4 px-8 flex-none">Full stack trace</h3>
36
+ <h3 class="font-bold mb-4 px-8 mt-8 flex-none">Full stack trace</h3>
22
37
  <div class="text-xs font-mono flex-grow h-full overflow-hidden">
23
- <div class="h-full overflow-auto px-8 pb-10 text-gray-400">
38
+ <div class="h-full overflow-auto px-8 pb-10 text-gray-400 leading-relaxed">
24
39
  <% error.backtrace.each do |line| %>
25
- <div class="py-1 hover:text-gray-900 transition-colors duration-100">
40
+ <div class="hover:text-gray-900 transition-colors duration-100">
26
41
  <%= line %>
27
42
  </div>
28
43
  <% end %>
@@ -5,7 +5,8 @@ module Lookbook
5
5
  module CodeFormatter
6
6
  class << self
7
7
  def highlight(source, language, opts = {})
8
- source&.gsub!("&gt;", "<")&.gsub!("&lt;", ">")
8
+ source&.strip! unless opts[:strip] == false
9
+ source&.gsub!("&gt;", ">")&.gsub!("&lt;", "<")
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)
@@ -2,15 +2,63 @@ module Lookbook
2
2
  class Error < StandardError
3
3
  delegate :full_message, :backtrace, :to_s, to: :target
4
4
 
5
- def initialize(original = nil, title: nil, message: nil, file_name: false, line_number: false)
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)
6
8
  @original = original
7
9
  @title = title
8
10
  @message = message
11
+ @file_path = file_path
9
12
  @file_name = file_name
10
13
  @line_number = line_number
14
+ @source_code = source_code
11
15
  super()
12
16
  end
13
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
+
14
62
  def title
15
63
  @title || target.class.to_s
16
64
  end
@@ -20,20 +68,29 @@ module Lookbook
20
68
  end
21
69
 
22
70
  def file_name
23
- path = if @file_name == false
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?
24
80
  parsed_backtrace[0][0] if parsed_backtrace.any?
25
81
  else
26
- @file_name
82
+ @file_path.presence || nil
27
83
  end
28
- path.delete_prefix("#{Rails.root}/")
84
+ path.nil? ? nil : path.to_s.delete_prefix("#{Rails.root}/")
29
85
  end
30
86
 
31
87
  def line_number
32
- if @line_number == false
88
+ number = if @line_number.nil?
33
89
  parsed_backtrace[0][1] if parsed_backtrace.any?
34
90
  else
35
- @line_number
91
+ @line_number.presence || nil
36
92
  end
93
+ number.present? ? number.to_i : number
37
94
  end
38
95
 
39
96
  def parsed_backtrace
@@ -48,5 +105,17 @@ module Lookbook
48
105
  def target
49
106
  @original.presence || self
50
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
51
120
  end
52
121
  end
data/lib/lookbook/page.rb CHANGED
@@ -20,6 +20,7 @@ module Lookbook
20
20
  def initialize(path, base_path)
21
21
  @pathname = Pathname.new path
22
22
  @base_path = base_path
23
+ @options = nil
23
24
  @errors = []
24
25
  end
25
26
 
@@ -61,7 +62,7 @@ module Lookbook
61
62
  end
62
63
 
63
64
  def content
64
- @content ||= strip_frontmatter(file_contents)
65
+ @content ||= strip_frontmatter(file_contents).strip
65
66
  end
66
67
 
67
68
  def matchers
@@ -104,23 +105,24 @@ module Lookbook
104
105
  frontmatter = (get_frontmatter(file_contents) || {}).deep_symbolize_keys
105
106
  rescue => exception
106
107
  frontmatter = {}
108
+ line_number_match = exception.message.match(/.*line\s(\d+)/)
107
109
  @errors.push(Lookbook::Error.new(exception, {
108
110
  title: "YAML frontmatter parsing error",
109
- file_name: @pathname.to_s,
110
- line_number: nil
111
+ file_path: @pathname.to_s,
112
+ line_number: line_number_match ? line_number_match[1] : false
111
113
  }))
112
114
  end
113
- options = Lookbook.config.page_options.deep_merge(frontmatter).with_indifferent_access
114
- options[:id] = options[:id] ? generate_id(options[:id]) : generate_id(lookup_path)
115
- options[:label] ||= name.titleize
116
- options[:title] ||= options[:label]
117
- options[:hidden] ||= false
118
- options[:landing] ||= false
119
- options[:position] = options[:position] ? options[:position].to_i : get_position_prefix(path_name)
120
- options[:markdown] ||= markdown_file?
121
- options[:header] = true unless options.key? :header
122
- options[:footer] = true unless options.key? :footer
123
- @options ||= options
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
124
126
  end
125
127
 
126
128
  def path_name
@@ -68,7 +68,7 @@ module Lookbook
68
68
  end
69
69
 
70
70
  def preview_paths
71
- ViewComponent::Preview.preview_paths
71
+ ViewComponent::Base.preview_paths
72
72
  end
73
73
 
74
74
  def parent_collections_names
@@ -143,7 +143,8 @@ module Lookbook
143
143
  Rails.logger.error "[lookbook] preview error\n#{exception.full_message}\n"
144
144
  @errors.push(Lookbook::Error.new(exception, {
145
145
  title: "Preview #{exception.class}",
146
- file_name: file[:rel_path]
146
+ file_name: file[:rel_path],
147
+ file_path: file[:path]
147
148
  }))
148
149
  end
149
150
  end
@@ -3,7 +3,7 @@ module Lookbook
3
3
  include Utils
4
4
 
5
5
  attr_reader :name, :preview
6
- delegate :params, :position, :group, :notes, :hidden?, to: :@example_inspector
6
+ delegate :params, :position, :group, :notes, :hidden?, :source, to: :@example_inspector
7
7
 
8
8
  def initialize(name, preview)
9
9
  @name = name
@@ -1,3 +1,3 @@
1
1
  module Lookbook
2
- VERSION = "0.7.2.beta.0"
2
+ VERSION = "0.7.2.beta.1"
3
3
  end