view_component 2.82.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of view_component might be problematic. Click here for more details.

@@ -31,50 +31,58 @@ module ViewComponent
31
31
  return if component_class == ViewComponent::Base
32
32
 
33
33
  component_class.superclass.compile(raise_errors: raise_errors) if should_compile_superclass?
34
- subclass_instance_methods = component_class.instance_methods(false)
35
-
36
- if subclass_instance_methods.include?(:with_content) && raise_errors
37
- raise ViewComponent::ComponentError.new(
38
- "#{component_class} implements a reserved method, `#with_content`.\n\n" \
39
- "To fix this issue, change the name of the method."
40
- )
41
- end
42
34
 
43
35
  if template_errors.present?
44
- raise ViewComponent::TemplateError.new(template_errors) if raise_errors
36
+ raise TemplateError.new(template_errors) if raise_errors
45
37
 
46
38
  return false
47
39
  end
48
40
 
49
- if subclass_instance_methods.include?(:before_render_check)
50
- ViewComponent::Deprecation.deprecation_warning(
51
- "`before_render_check`", :"`before_render`"
52
- )
53
- end
54
-
55
41
  if raise_errors
56
42
  component_class.validate_initialization_parameters!
57
43
  component_class.validate_collection_parameter!
58
44
  end
59
45
 
60
- templates.each do |template|
61
- # Remove existing compiled template methods,
62
- # as Ruby warns when redefining a method.
63
- method_name = call_method_name(template[:variant])
46
+ if has_inline_template?
47
+ template = component_class.inline_template
64
48
 
65
49
  redefinition_lock.synchronize do
66
- component_class.silence_redefinition_of_method(method_name)
50
+ component_class.silence_redefinition_of_method("call")
67
51
  # rubocop:disable Style/EvalWithLocation
68
- component_class.class_eval <<-RUBY, template[:path], 0
69
- def #{method_name}
70
- #{compiled_template(template[:path])}
52
+ component_class.class_eval <<-RUBY, template.path, template.lineno
53
+ def call
54
+ #{compiled_inline_template(template)}
71
55
  end
72
56
  RUBY
73
57
  # rubocop:enable Style/EvalWithLocation
58
+
59
+ component_class.silence_redefinition_of_method("render_template_for")
60
+ component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
61
+ def render_template_for(variant = nil)
62
+ call
63
+ end
64
+ RUBY
65
+ end
66
+ else
67
+ templates.each do |template|
68
+ # Remove existing compiled template methods,
69
+ # as Ruby warns when redefining a method.
70
+ method_name = call_method_name(template[:variant])
71
+
72
+ redefinition_lock.synchronize do
73
+ component_class.silence_redefinition_of_method(method_name)
74
+ # rubocop:disable Style/EvalWithLocation
75
+ component_class.class_eval <<-RUBY, template[:path], 0
76
+ def #{method_name}
77
+ #{compiled_template(template[:path])}
78
+ end
79
+ RUBY
80
+ # rubocop:enable Style/EvalWithLocation
81
+ end
74
82
  end
75
- end
76
83
 
77
- define_render_template_for
84
+ define_render_template_for
85
+ end
78
86
 
79
87
  component_class.build_i18n_backend
80
88
 
@@ -109,12 +117,16 @@ module ViewComponent
109
117
  end
110
118
  end
111
119
 
120
+ def has_inline_template?
121
+ component_class.respond_to?(:inline_template) && component_class.inline_template.present?
122
+ end
123
+
112
124
  def template_errors
113
125
  @__vc_template_errors ||=
114
126
  begin
115
127
  errors = []
116
128
 
117
- if (templates + inline_calls).empty?
129
+ if (templates + inline_calls).empty? && !has_inline_template?
118
130
  errors << "Couldn't find a template file or inline render method for #{component_class}."
119
131
  end
120
132
 
@@ -222,9 +234,21 @@ module ViewComponent
222
234
  end
223
235
  end
224
236
 
237
+ def compiled_inline_template(template)
238
+ handler = ActionView::Template.handler_for_extension(template.language)
239
+ template.rstrip! if component_class.strip_trailing_whitespace?
240
+
241
+ compile_template(template.source, handler)
242
+ end
243
+
225
244
  def compiled_template(file_path)
226
245
  handler = ActionView::Template.handler_for_extension(File.extname(file_path).delete("."))
227
246
  template = File.read(file_path)
247
+
248
+ compile_template(template, handler)
249
+ end
250
+
251
+ def compile_template(template, handler)
228
252
  template.rstrip! if component_class.strip_trailing_whitespace?
229
253
 
230
254
  if handler.method(:call).parameters.length > 1
@@ -253,8 +277,7 @@ module ViewComponent
253
277
  end
254
278
 
255
279
  def should_compile_superclass?
256
- development? &&
257
- templates.empty? &&
280
+ development? && templates.empty? && !has_inline_template? &&
258
281
  !(
259
282
  component_class.instance_methods(false).include?(:call) ||
260
283
  component_class.private_instance_methods(false).include?(:call)
@@ -23,7 +23,8 @@ module ViewComponent
23
23
  show_previews: Rails.env.development? || Rails.env.test?,
24
24
  preview_paths: default_preview_paths,
25
25
  test_controller: "ApplicationController",
26
- default_preview_layout: nil
26
+ default_preview_layout: nil,
27
+ capture_compatibility_patch_enabled: false
27
28
  })
28
29
  end
29
30
 
@@ -126,9 +127,6 @@ module ViewComponent
126
127
  # The locations in which component previews will be looked up.
127
128
  # Defaults to `['test/component/previews']` relative to your Rails root.
128
129
 
129
- # @!attribute preview_path
130
- # @deprecated Use #preview_paths instead. Will be removed in v3.0.0.
131
-
132
130
  # @!attribute test_controller
133
131
  # @return [String]
134
132
  # The controller used for testing components.
@@ -140,6 +138,13 @@ module ViewComponent
140
138
  # A custom default layout used for the previews index page and individual
141
139
  # previews.
142
140
  # Defaults to `nil`. If this is falsy, `"component_preview"` is used.
141
+ #
142
+ # @!attribute capture_compatibility_patch_enabled
143
+ # @return [Boolean]
144
+ # Enables the experimental capture compatibility patch that makes ViewComponent
145
+ # compatible with forms, capture, and other built-ins.
146
+ # previews.
147
+ # Defaults to `false`.
143
148
 
144
149
  def default_preview_paths
145
150
  return [] unless defined?(Rails.root) && Dir.exist?("#{Rails.root}/test/components/previews")
@@ -158,15 +163,6 @@ module ViewComponent
158
163
  @config = self.class.defaults
159
164
  end
160
165
 
161
- def preview_path
162
- preview_paths
163
- end
164
-
165
- def preview_path=(new_value)
166
- ViewComponent::Deprecation.deprecation_warning("`preview_path`", :"`preview_paths`")
167
- self.preview_paths = Array.wrap(new_value)
168
- end
169
-
170
166
  delegate_missing_to :config
171
167
 
172
168
  private
@@ -3,6 +3,6 @@
3
3
  require "active_support/deprecation"
4
4
 
5
5
  module ViewComponent
6
- DEPRECATION_HORIZON = "3.0.0"
6
+ DEPRECATION_HORIZON = "4.0.0"
7
7
  Deprecation = ActiveSupport::Deprecation.new(DEPRECATION_HORIZON, "ViewComponent")
8
8
  end
@@ -12,7 +12,11 @@ nav_order: 3
12
12
  ## <%= section.heading %>
13
13
 
14
14
  <% section.methods.each do |method| %>
15
- ### <%== render ViewComponent::DocsBuilderComponent::MethodDoc.new(method) %>
15
+ ### <%== render ViewComponent::DocsBuilderComponent::MethodDoc.new(method, section.show_types) %>
16
+
17
+ <% end %>
18
+ <% section.error_klasses.each do |error_klass| %>
19
+ ### <%== render ViewComponent::DocsBuilderComponent::ErrorKlassDoc.new(error_klass, section.show_types) %>
16
20
 
17
21
  <% end %>
18
22
  <% end %>
@@ -2,21 +2,40 @@
2
2
 
3
3
  module ViewComponent
4
4
  class DocsBuilderComponent < Base
5
- class Section < Struct.new(:heading, :methods, :show_types, keyword_init: true)
6
- def initialize(heading: nil, methods: [], show_types: true)
5
+ class Section < Struct.new(:heading, :methods, :error_klasses, :show_types, keyword_init: true)
6
+ def initialize(heading: nil, methods: [], error_klasses: [], show_types: true)
7
7
  methods.sort_by! { |method| method[:name] }
8
+ error_klasses.sort!
8
9
  super
9
10
  end
10
11
  end
11
12
 
12
- class MethodDoc < ViewComponent::Base
13
- def initialize(method, section: Section.new(show_types: true))
14
- @method = method
15
- @section = section
13
+ class ErrorKlassDoc < ViewComponent::Base
14
+ def initialize(error_klass, _show_types)
15
+ @error_klass = error_klass
16
+ end
17
+
18
+ def klass_name
19
+ @error_klass.gsub("ViewComponent::", "").gsub("::MESSAGE", "")
16
20
  end
17
21
 
18
- def show_types?
19
- @section.show_types
22
+ def error_message
23
+ ViewComponent.const_get(@error_klass)
24
+ end
25
+
26
+ def call
27
+ <<~DOCS.chomp
28
+ `#{klass_name}`
29
+
30
+ #{error_message}
31
+ DOCS
32
+ end
33
+ end
34
+
35
+ class MethodDoc < ViewComponent::Base
36
+ def initialize(method, show_types = true)
37
+ @method = method
38
+ @show_types = show_types
20
39
  end
21
40
 
22
41
  def deprecated?
@@ -28,7 +47,7 @@ module ViewComponent
28
47
  end
29
48
 
30
49
  def types
31
- " → [#{@method.tag(:return).types.join(",")}]" if @method.tag(:return)&.types && show_types?
50
+ " → [#{@method.tag(:return).types.join(",")}]" if @method.tag(:return)&.types && @show_types
32
51
  end
33
52
 
34
53
  def signature_or_name
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails"
4
- require "view_component/base"
4
+ require "view_component/config"
5
5
 
6
6
  module ViewComponent
7
7
  class Engine < Rails::Engine # :nodoc:
8
- config.view_component = ViewComponent::Base.config
8
+ config.view_component = ViewComponent::Config.defaults
9
9
 
10
10
  rake_tasks do
11
11
  load "view_component/rails/tasks/view_component.rake"
@@ -14,9 +14,6 @@ module ViewComponent
14
14
  initializer "view_component.set_configs" do |app|
15
15
  options = app.config.view_component
16
16
 
17
- %i[generate preview_controller preview_route show_previews_source].each do |config_option|
18
- options[config_option] ||= ViewComponent::Base.public_send(config_option)
19
- end
20
17
  options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
21
18
  options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil?
22
19
  options.show_previews = (Rails.env.development? || Rails.env.test?) if options.show_previews.nil?
@@ -39,6 +36,8 @@ module ViewComponent
39
36
 
40
37
  initializer "view_component.enable_instrumentation" do |app|
41
38
  ActiveSupport.on_load(:view_component) do
39
+ Base.config = app.config.view_component
40
+
42
41
  if app.config.view_component.instrumentation_enabled.present?
43
42
  # :nocov:
44
43
  ViewComponent::Base.prepend(ViewComponent::Instrumentation)
@@ -47,6 +46,14 @@ module ViewComponent
47
46
  end
48
47
  end
49
48
 
49
+ # :nocov:
50
+ initializer "view_component.enable_capture_patch" do |app|
51
+ ActiveSupport.on_load(:view_component) do
52
+ ActionView::Base.include(ViewComponent::CaptureCompatibility) if app.config.view_component.capture_compatibility_patch_enabled
53
+ end
54
+ end
55
+ # :nocov:
56
+
50
57
  initializer "view_component.set_autoload_paths" do |app|
51
58
  options = app.config.view_component
52
59
 
@@ -143,20 +150,3 @@ module ViewComponent
143
150
  end
144
151
  end
145
152
  end
146
-
147
- if RUBY_VERSION < "2.7.0"
148
- ViewComponent::Deprecation.deprecation_warning("Support for Ruby versions < 2.7.0")
149
- end
150
-
151
- # :nocov:
152
- unless defined?(ViewComponent::Base)
153
- require "view_component/deprecation"
154
-
155
- ViewComponent::Deprecation.deprecation_warning(
156
- "Manually loading the engine",
157
- "remove `require \"view_component/engine\"`"
158
- )
159
-
160
- require "view_component"
161
- end
162
- # :nocov:
@@ -0,0 +1,213 @@
1
+ module ViewComponent
2
+ class BaseError < StandardError
3
+ def initialize
4
+ super(self.class::MESSAGE)
5
+ end
6
+ end
7
+
8
+ class DuplicateSlotContentError < StandardError
9
+ MESSAGE =
10
+ "It looks like a block was provided after calling `with_content` on COMPONENT, " \
11
+ "which means that ViewComponent doesn't know which content to use.\n\n" \
12
+ "To fix this issue, use either `with_content` or a block."
13
+
14
+ def initialize(klass_name)
15
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s))
16
+ end
17
+ end
18
+
19
+ class TemplateError < StandardError
20
+ def initialize(errors)
21
+ super(errors.join(", "))
22
+ end
23
+ end
24
+
25
+ class MultipleInlineTemplatesError < BaseError
26
+ MESSAGE = "Inline templates can only be defined once per-component."
27
+ end
28
+
29
+ class MissingPreviewTemplateError < StandardError
30
+ MESSAGE =
31
+ "A preview template for example EXAMPLE doesn't exist.\n\n" \
32
+ "To fix this issue, create a template for the example."
33
+
34
+ def initialize(example)
35
+ super(MESSAGE.gsub("EXAMPLE", example))
36
+ end
37
+ end
38
+
39
+ class DuplicateContentError < StandardError
40
+ MESSAGE =
41
+ "It looks like a block was provided after calling `with_content` on COMPONENT, " \
42
+ "which means that ViewComponent doesn't know which content to use.\n\n" \
43
+ "To fix this issue, use either `with_content` or a block."
44
+
45
+ def initialize(klass_name)
46
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s))
47
+ end
48
+ end
49
+
50
+ class EmptyOrInvalidInitializerError < StandardError
51
+ MESSAGE =
52
+ "The COMPONENT initializer is empty or invalid. " \
53
+ "It must accept the parameter `PARAMETER` to render it as a collection.\n\n" \
54
+ "To fix this issue, update the initializer to accept `PARAMETER`.\n\n" \
55
+ "See [the collections docs](https://viewcomponent.org/guide/collections.html) for more information on rendering collections."
56
+
57
+ def initialize(klass_name, parameter)
58
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s))
59
+ end
60
+ end
61
+
62
+ class MissingCollectionArgumentError < StandardError
63
+ MESSAGE =
64
+ "The initializer for COMPONENT doesn't accept the parameter `PARAMETER`, " \
65
+ "which is required to render it as a collection.\n\n" \
66
+ "To fix this issue, update the initializer to accept `PARAMETER`.\n\n" \
67
+ "See [the collections docs](https://viewcomponent.org/guide/collections.html) for more information on rendering collections."
68
+
69
+ def initialize(klass_name, parameter)
70
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s))
71
+ end
72
+ end
73
+
74
+ class ReservedParameterError < StandardError
75
+ MESSAGE =
76
+ "COMPONENT initializer can't accept the parameter `PARAMETER`, as it will override a " \
77
+ "public ViewComponent method. To fix this issue, rename the parameter."
78
+
79
+ def initialize(klass_name, parameter)
80
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s))
81
+ end
82
+ end
83
+
84
+ class InvalidCollectionArgumentError < BaseError
85
+ MESSAGE =
86
+ "The value of the first argument passed to `with_collection` isn't a valid collection. " \
87
+ "Make sure it responds to `to_ary`."
88
+ end
89
+
90
+ class ContentSlotNameError < StandardError
91
+ MESSAGE =
92
+ "COMPONENT declares a slot named content, which is a reserved word in ViewComponent.\n\n" \
93
+ "Content passed to a ViewComponent as a block is captured and assigned to the `content` accessor without having to create an explicit slot.\n\n" \
94
+ "To fix this issue, either use the `content` accessor directly or choose a different slot name."
95
+
96
+ def initialize(klass_name)
97
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s))
98
+ end
99
+ end
100
+
101
+ class InvalidSlotDefinitionError < BaseError
102
+ MESSAGE =
103
+ "Invalid slot definition. Please pass a class, " \
104
+ "string, or callable (that is proc, lambda, etc)"
105
+ end
106
+
107
+ class SlotPredicateNameError < StandardError
108
+ MESSAGE =
109
+ "COMPONENT declares a slot named SLOT_NAME, which ends with a question mark.\n\n" \
110
+ "This isn't allowed because the ViewComponent framework already provides predicate " \
111
+ "methods ending in `?`.\n\n" \
112
+ "To fix this issue, choose a different name."
113
+
114
+ def initialize(klass_name, slot_name)
115
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
116
+ end
117
+ end
118
+
119
+ class RedefinedSlotError < StandardError
120
+ MESSAGE =
121
+ "COMPONENT declares the SLOT_NAME slot multiple times.\n\n" \
122
+ "To fix this issue, choose a different slot name."
123
+
124
+ def initialize(klass_name, slot_name)
125
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
126
+ end
127
+ end
128
+
129
+ class ReservedSingularSlotNameError < StandardError
130
+ MESSAGE =
131
+ "COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \
132
+ "To fix this issue, choose a different name."
133
+
134
+ def initialize(klass_name, slot_name)
135
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
136
+ end
137
+ end
138
+
139
+ class ReservedPluralSlotNameError < StandardError
140
+ MESSAGE =
141
+ "COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \
142
+ "To fix this issue, choose a different name."
143
+
144
+ def initialize(klass_name, slot_name)
145
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
146
+ end
147
+ end
148
+
149
+ class ContentAlreadySetForPolymorphicSlotError < StandardError
150
+ MESSAGE = "Content for slot SLOT_NAME has already been provided."
151
+
152
+ def initialize(slot_name)
153
+ super(MESSAGE.gsub("SLOT_NAME", slot_name.to_s))
154
+ end
155
+ end
156
+
157
+ class NilWithContentError < BaseError
158
+ MESSAGE =
159
+ "No content provided to `#with_content` for #{self}.\n\n" \
160
+ "To fix this issue, pass a value."
161
+ end
162
+
163
+ class TranslateCalledBeforeRenderError < BaseError
164
+ MESSAGE =
165
+ "`#translate` can't be used during initialization as it depends " \
166
+ "on the view context that only exists once a ViewComponent is passed to " \
167
+ "the Rails render pipeline.\n\n" \
168
+ "It's sometimes possible to fix this issue by moving code dependent on " \
169
+ "`#translate` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
170
+ end
171
+
172
+ class HelpersCalledBeforeRenderError < BaseError
173
+ MESSAGE =
174
+ "`#helpers` can't be used during initialization as it depends " \
175
+ "on the view context that only exists once a ViewComponent is passed to " \
176
+ "the Rails render pipeline.\n\n" \
177
+ "It's sometimes possible to fix this issue by moving code dependent on " \
178
+ "`#helpers` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
179
+ end
180
+
181
+ class ControllerCalledBeforeRenderError < BaseError
182
+ MESSAGE =
183
+ "`#controller` can't be used during initialization, as it depends " \
184
+ "on the view context that only exists once a ViewComponent is passed to " \
185
+ "the Rails render pipeline.\n\n" \
186
+ "It's sometimes possible to fix this issue by moving code dependent on " \
187
+ "`#controller` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
188
+ end
189
+
190
+ class NoMatchingTemplatesForPreviewError < StandardError
191
+ MESSAGE = "Found 0 matches for templates for TEMPLATE_IDENTIFIER."
192
+
193
+ def initialize(template_identifier)
194
+ super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
195
+ end
196
+ end
197
+
198
+ class MultipleMatchingTemplatesForPreviewError < StandardError
199
+ MESSAGE = "Found multiple templates for TEMPLATE_IDENTIFIER."
200
+
201
+ def initialize(template_identifier)
202
+ super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
203
+ end
204
+ end
205
+
206
+ class SystemTestControllerOnlyAllowedInTestError < BaseError
207
+ MESSAGE = "ViewComponent SystemTest controller must only be called in a test environment for security reasons."
208
+ end
209
+
210
+ class SystemTestControllerNefariousPathError < BaseError
211
+ MESSAGE = "ViewComponent SystemTest controller attempted to load a file outside of the expected directory."
212
+ end
213
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent # :nodoc:
4
+ module InlineTemplate
5
+ extend ActiveSupport::Concern
6
+ Template = Struct.new(:source, :language, :path, :lineno)
7
+
8
+ class_methods do
9
+ def method_missing(method, *args)
10
+ return super if !method.end_with?("_template")
11
+
12
+ if defined?(@__vc_inline_template_defined) && @__vc_inline_template_defined
13
+ raise MultipleInlineTemplatesError
14
+ end
15
+
16
+ if args.size != 1
17
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)"
18
+ end
19
+
20
+ ext = method.to_s.gsub("_template", "")
21
+ template = args.first
22
+
23
+ @__vc_inline_template_language = ext
24
+
25
+ caller = caller_locations(1..1)[0]
26
+ @__vc_inline_template = Template.new(
27
+ template,
28
+ ext,
29
+ caller.absolute_path || caller.path,
30
+ caller.lineno
31
+ )
32
+
33
+ @__vc_inline_template_defined = true
34
+ end
35
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
36
+
37
+ def respond_to_missing?(method, include_all = false)
38
+ method.end_with?("_template") || super
39
+ end
40
+
41
+ def inline_template
42
+ @__vc_inline_template
43
+ end
44
+
45
+ def inline_template_language
46
+ @__vc_inline_template_language if defined?(@__vc_inline_template_language)
47
+ end
48
+
49
+ def inherited(subclass)
50
+ super
51
+ subclass.instance_variable_set(:@__vc_inline_template_language, inline_template_language)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -80,13 +80,7 @@ module ViewComponent # :nodoc:
80
80
  Dir["#{path}/#{preview_name}_preview/#{example}.html.*"].first
81
81
  end
82
82
 
83
- if preview_path.nil?
84
- raise(
85
- PreviewTemplateError,
86
- "A preview template for example #{example} doesn't exist.\n\n" \
87
- "To fix this issue, create a template for the example."
88
- )
89
- end
83
+ raise MissingPreviewTemplateError.new(example) if preview_path.nil?
90
84
 
91
85
  path = Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
92
86
  Pathname.new(path)
@@ -3,7 +3,7 @@
3
3
  task stats: "view_component:statsetup"
4
4
 
5
5
  namespace :view_component do
6
- task statsetup: :environment do
6
+ task :statsetup do
7
7
  require "rails/code_statistics"
8
8
 
9
9
  ::STATS_DIRECTORIES << ["ViewComponents", ViewComponent::Base.view_component_path]