lookbook 0.7.2.beta.0 → 0.7.2.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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