view_component 3.16.0 → 3.17.0

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: 7e3b39556c40610939b3bb6418c79ee0352d78b4e1ea28cdca0c7aa14cd889a6
4
- data.tar.gz: 1be4c90a4cecd14ce38ac1aa9c45646415ed81c55a8cf922ec119b3cd5833c10
3
+ metadata.gz: 5f95cb571165fe8ad9d61f6236c5104ca6b9becec9c45e1f5bbb93f2e8bad487
4
+ data.tar.gz: 2f547097278242f1b1c945341b0cbcefd455ce93d2ed61145209f92a7acd5c2b
5
5
  SHA512:
6
- metadata.gz: 585dddfd0449d8d73652ce8b94f818c312e86316e77c6766e157ab4c025a55f8b896a2b9ee8f0fbc649128594d8c24b138515a0f3d3172e227f57a60b47e6e79
7
- data.tar.gz: 48833cc160e6ad0a747da98f5efc959d0425895539886f14efb02cd447268306b6ce700ce50e501e520a3842b96abeeaab5e33e96751826795f59497f3326782
6
+ metadata.gz: 51f0cbc8e8794c383cc2bbb97736e7be0da272c340c94ef9cf3927dd4526b58f73566aec5e1e37f0ee2a302fb99f0fec9a7f058620e6c051339db83426c8c852
7
+ data.tar.gz: 8484fa046329adf95fd78946ad5ce4b1ab647d9f6e7546da4bee02fe56fb74f25b8ad35cffee4afdae726d99832ebbcfae47a056603c6cfa1c9df7ef1e6476f4
data/docs/CHANGELOG.md CHANGED
@@ -10,8 +10,30 @@ nav_order: 5
10
10
 
11
11
  ## main
12
12
 
13
+ ## 3.17.0
14
+
15
+ * Use struct instead openstruct in lib code.
16
+
17
+ *Oleksii Vasyliev*
18
+
19
+ * Fix bug where stimulus controller was not added to ERB when stimulus was activated by default.
20
+
21
+ *Denis Pasin*
22
+
23
+ * Add typescript support to stimulus generator.
24
+
25
+ *Denis Pasin*
26
+
27
+ * Fix the example of #vc_test_request in the API reference to use the correct method name.
28
+
29
+ *Alberto Rocha*
30
+
13
31
  ## 3.16.0
14
32
 
33
+ * Fix development mode race condition that caused an invalid duplicate template error.
34
+
35
+ *Blake Williams*
36
+
15
37
  * Add template information to multiple template error messages.
16
38
 
17
39
  *Joel Hawksley*
@@ -33,7 +33,7 @@ module ViewComponent
33
33
  end
34
34
 
35
35
  def stimulus_controller
36
- if options["stimulus"]
36
+ if stimulus?
37
37
  File.join(destination_directory, destination_file_name)
38
38
  .sub("#{component_path}/", "")
39
39
  .tr("_", "-")
@@ -44,5 +44,13 @@ module ViewComponent
44
44
  def sidecar?
45
45
  options["sidecar"] || ViewComponent::Base.config.generate.sidecar
46
46
  end
47
+
48
+ def stimulus?
49
+ options["stimulus"] || ViewComponent::Base.config.generate.stimulus_controller
50
+ end
51
+
52
+ def typescript?
53
+ options["typescript"] || ViewComponent::Base.config.generate.typescript
54
+ end
47
55
  end
48
56
  end
@@ -24,7 +24,7 @@ module Erb
24
24
  private
25
25
 
26
26
  def data_attributes
27
- if options["stimulus"]
27
+ if stimulus?
28
28
  " data-controller=\"#{stimulus_controller}\""
29
29
  end
30
30
  end
@@ -9,9 +9,10 @@ module Stimulus
9
9
 
10
10
  source_root File.expand_path("templates", __dir__)
11
11
  class_option :sidecar, type: :boolean, default: false
12
+ class_option :typescript, type: :boolean, default: false
12
13
 
13
14
  def create_stimulus_controller
14
- template "component_controller.js", destination
15
+ template "component_controller.#{filetype}", destination
15
16
  end
16
17
 
17
18
  def stimulus_module
@@ -22,11 +23,15 @@ module Stimulus
22
23
 
23
24
  private
24
25
 
26
+ def filetype
27
+ typescript? ? "ts" : "js"
28
+ end
29
+
25
30
  def destination
26
31
  if sidecar?
27
- File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component_controller.js")
32
+ File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component_controller.#{filetype}")
28
33
  else
29
- File.join(component_path, class_path, "#{file_name}_component_controller.js")
34
+ File.join(component_path, class_path, "#{file_name}_component_controller.#{filetype}")
30
35
  end
31
36
  end
32
37
 
@@ -0,0 +1,9 @@
1
+ import { Controller } from "<%= stimulus_module %>";
2
+
3
+ export default class extends Controller {
4
+ declare element: HTMLElement;
5
+
6
+ connect() {
7
+ console.log("Hello, Stimulus!", this.element);
8
+ }
9
+ }
@@ -412,6 +412,14 @@ module ViewComponent
412
412
  # config.view_component.generate.stimulus_controller = true
413
413
  # ```
414
414
  #
415
+ # #### `#typescript`
416
+ #
417
+ # Generate TypeScript files instead of JavaScript files:
418
+ #
419
+ # ```ruby
420
+ # config.view_component.generate.typescript = true
421
+ # ```
422
+ #
415
423
  # #### #locale
416
424
  #
417
425
  # Always generate translations file alongside the component:
@@ -30,7 +30,12 @@ module ViewComponent
30
30
  @component.superclass.compile(raise_errors: raise_errors)
31
31
  end
32
32
 
33
- return if gather_template_errors(raise_errors).any?
33
+ if template_errors.present?
34
+ raise TemplateError.new(template_errors) if raise_errors
35
+
36
+ # this return is load bearing, and prevents the component from being considered "compiled?"
37
+ return false
38
+ end
34
39
 
35
40
  if raise_errors
36
41
  @component.validate_initialization_parameters!
@@ -98,70 +103,70 @@ module ViewComponent
98
103
  end
99
104
  end
100
105
 
101
- def gather_template_errors(raise_errors)
102
- errors = []
106
+ def template_errors
107
+ @_template_errors ||= begin
108
+ errors = []
103
109
 
104
- errors << "Couldn't find a template file or inline render method for #{@component}." if @templates.empty?
110
+ errors << "Couldn't find a template file or inline render method for #{@component}." if @templates.empty?
105
111
 
106
- # We currently allow components to have both an inline call method and a template for a variant, with the
107
- # inline call method overriding the template. We should aim to change this in v4 to instead
108
- # raise an error.
109
- @templates.reject(&:inline_call?)
110
- .map { |template| [template.variant, template.format] }
111
- .tally
112
- .select { |_, count| count > 1 }
113
- .each do |tally|
114
- variant, this_format = tally.first
112
+ # We currently allow components to have both an inline call method and a template for a variant, with the
113
+ # inline call method overriding the template. We should aim to change this in v4 to instead
114
+ # raise an error.
115
+ @templates.reject(&:inline_call?)
116
+ .map { |template| [template.variant, template.format] }
117
+ .tally
118
+ .select { |_, count| count > 1 }
119
+ .each do |tally|
120
+ variant, this_format = tally.first
115
121
 
116
- variant_string = " for variant `#{variant}`" if variant.present?
117
-
118
- errors << "More than one #{this_format.upcase} template found#{variant_string} for #{@component}. "
119
- end
122
+ variant_string = " for variant `#{variant}`" if variant.present?
120
123
 
121
- default_template_types = @templates.each_with_object(Set.new) do |template, memo|
122
- next if template.variant
124
+ errors << "More than one #{this_format.upcase} template found#{variant_string} for #{@component}. "
125
+ end
123
126
 
124
- memo << :template_file if !template.inline_call?
125
- memo << :inline_render if template.inline_call? && template.defined_on_self?
127
+ default_template_types = @templates.each_with_object(Set.new) do |template, memo|
128
+ next if template.variant
126
129
 
127
- memo
128
- end
130
+ memo << :template_file if !template.inline_call?
131
+ memo << :inline_render if template.inline_call? && template.defined_on_self?
129
132
 
130
- if default_template_types.length > 1
131
- errors <<
132
- "Template file and inline render method found for #{@component}. " \
133
- "There can only be a template file or inline render method per component."
134
- end
133
+ memo
134
+ end
135
135
 
136
- # If a template has inline calls, they can conflict with template files the component may use
137
- # to render. This attempts to catch and raise that issue before run time. For example,
138
- # `def render_mobile` would conflict with a sidecar template of `component.html+mobile.erb`
139
- duplicate_template_file_and_inline_call_variants =
140
- @templates.reject(&:inline_call?).map(&:variant) &
141
- @templates.select { _1.inline_call? && _1.defined_on_self? }.map(&:variant)
142
-
143
- unless duplicate_template_file_and_inline_call_variants.empty?
144
- count = duplicate_template_file_and_inline_call_variants.count
145
-
146
- errors <<
147
- "Template #{"file".pluralize(count)} and inline render #{"method".pluralize(count)} " \
148
- "found for #{"variant".pluralize(count)} " \
149
- "#{duplicate_template_file_and_inline_call_variants.map { |v| "'#{v}'" }.to_sentence} " \
150
- "in #{@component}. There can only be a template file or inline render method per variant."
151
- end
136
+ if default_template_types.length > 1
137
+ errors <<
138
+ "Template file and inline render method found for #{@component}. " \
139
+ "There can only be a template file or inline render method per component."
140
+ end
152
141
 
153
- @templates.select(&:variant).each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |template, memo|
154
- memo[template.normalized_variant_name] << template.variant
155
- memo
156
- end.each do |_, variant_names|
157
- next unless variant_names.length > 1
142
+ # If a template has inline calls, they can conflict with template files the component may use
143
+ # to render. This attempts to catch and raise that issue before run time. For example,
144
+ # `def render_mobile` would conflict with a sidecar template of `component.html+mobile.erb`
145
+ duplicate_template_file_and_inline_call_variants =
146
+ @templates.reject(&:inline_call?).map(&:variant) &
147
+ @templates.select { _1.inline_call? && _1.defined_on_self? }.map(&:variant)
148
+
149
+ unless duplicate_template_file_and_inline_call_variants.empty?
150
+ count = duplicate_template_file_and_inline_call_variants.count
151
+
152
+ errors <<
153
+ "Template #{"file".pluralize(count)} and inline render #{"method".pluralize(count)} " \
154
+ "found for #{"variant".pluralize(count)} " \
155
+ "#{duplicate_template_file_and_inline_call_variants.map { |v| "'#{v}'" }.to_sentence} " \
156
+ "in #{@component}. There can only be a template file or inline render method per variant."
157
+ end
158
158
 
159
- errors << "Colliding templates #{variant_names.sort.map { |v| "'#{v}'" }.to_sentence} found in #{@component}."
160
- end
159
+ @templates.select(&:variant).each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |template, memo|
160
+ memo[template.normalized_variant_name] << template.variant
161
+ memo
162
+ end.each do |_, variant_names|
163
+ next unless variant_names.length > 1
161
164
 
162
- raise TemplateError.new(errors, @templates) if errors.any? && raise_errors
165
+ errors << "Colliding templates #{variant_names.sort.map { |v| "'#{v}'" }.to_sentence} found in #{@component}."
166
+ end
163
167
 
164
- errors
168
+ errors
169
+ end
165
170
  end
166
171
 
167
172
  def gather_templates
@@ -48,6 +48,12 @@ module ViewComponent
48
48
  #
49
49
  # config.view_component.generate.stimulus_controller = true
50
50
  #
51
+ # #### `#typescript`
52
+ #
53
+ # Generate TypeScript files instead of JavaScript files:
54
+ #
55
+ # config.view_component.generate.typescript = true
56
+ #
51
57
  # #### `#locale`
52
58
  #
53
59
  # Always generate translations file alongside the component:
@@ -20,12 +20,6 @@ module ViewComponent
20
20
  def initialize(errors, templates = nil)
21
21
  message = errors.join("\n")
22
22
 
23
- if templates
24
- message << "\n"
25
- message << "Templates:\n"
26
- message << templates.map(&:inspect).join("\n")
27
- end
28
-
29
23
  super(message)
30
24
  end
31
25
  end
@@ -1,7 +1,10 @@
1
- require "ostruct"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ViewComponent
4
4
  class Template
5
+ DataWithSource = Struct.new(:format, :identifier, :short_identifier, :type, keyword_init: true)
6
+ DataNoSource = Struct.new(:source, :identifier, :type, keyword_init: true)
7
+
5
8
  attr_reader :variant, :this_format, :type
6
9
 
7
10
  def initialize(
@@ -109,13 +112,13 @@ module ViewComponent
109
112
 
110
113
  if handler.method(:call).parameters.length > 1
111
114
  handler.call(
112
- OpenStruct.new(format: @this_format, identifier: @path, short_identifier: short_identifier, type: type),
115
+ DataWithSource.new(format: @this_format, identifier: @path, short_identifier: short_identifier, type: type),
113
116
  this_source
114
117
  )
115
118
  # :nocov:
116
119
  # TODO: Remove in v4
117
120
  else
118
- handler.call(OpenStruct.new(source: this_source, identifier: @path, type: type))
121
+ handler.call(DataNoSource.new(source: this_source, identifier: @path, type: type))
119
122
  end
120
123
  # :nocov:
121
124
  end
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 3
6
- MINOR = 16
6
+ MINOR = 17
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.16.0
4
+ version: 3.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ViewComponent Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-01 00:00:00.000000000 Z
11
+ date: 2024-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -492,6 +492,62 @@ dependencies:
492
492
  - - ">="
493
493
  - !ruby/object:Gem::Version
494
494
  version: '0'
495
+ - !ruby/object:Gem::Dependency
496
+ name: base64
497
+ requirement: !ruby/object:Gem::Requirement
498
+ requirements:
499
+ - - ">="
500
+ - !ruby/object:Gem::Version
501
+ version: '0'
502
+ type: :development
503
+ prerelease: false
504
+ version_requirements: !ruby/object:Gem::Requirement
505
+ requirements:
506
+ - - ">="
507
+ - !ruby/object:Gem::Version
508
+ version: '0'
509
+ - !ruby/object:Gem::Dependency
510
+ name: bigdecimal
511
+ requirement: !ruby/object:Gem::Requirement
512
+ requirements:
513
+ - - ">="
514
+ - !ruby/object:Gem::Version
515
+ version: '0'
516
+ type: :development
517
+ prerelease: false
518
+ version_requirements: !ruby/object:Gem::Requirement
519
+ requirements:
520
+ - - ">="
521
+ - !ruby/object:Gem::Version
522
+ version: '0'
523
+ - !ruby/object:Gem::Dependency
524
+ name: drb
525
+ requirement: !ruby/object:Gem::Requirement
526
+ requirements:
527
+ - - ">="
528
+ - !ruby/object:Gem::Version
529
+ version: '0'
530
+ type: :development
531
+ prerelease: false
532
+ version_requirements: !ruby/object:Gem::Requirement
533
+ requirements:
534
+ - - ">="
535
+ - !ruby/object:Gem::Version
536
+ version: '0'
537
+ - !ruby/object:Gem::Dependency
538
+ name: mutex_m
539
+ requirement: !ruby/object:Gem::Requirement
540
+ requirements:
541
+ - - ">="
542
+ - !ruby/object:Gem::Version
543
+ version: '0'
544
+ type: :development
545
+ prerelease: false
546
+ version_requirements: !ruby/object:Gem::Requirement
547
+ requirements:
548
+ - - ">="
549
+ - !ruby/object:Gem::Version
550
+ version: '0'
495
551
  - !ruby/object:Gem::Dependency
496
552
  name: ostruct
497
553
  requirement: !ruby/object:Gem::Requirement
@@ -543,6 +599,7 @@ files:
543
599
  - lib/rails/generators/slim/templates/component.html.slim.tt
544
600
  - lib/rails/generators/stimulus/component_generator.rb
545
601
  - lib/rails/generators/stimulus/templates/component_controller.js.tt
602
+ - lib/rails/generators/stimulus/templates/component_controller.ts.tt
546
603
  - lib/rails/generators/tailwindcss/component_generator.rb
547
604
  - lib/rails/generators/tailwindcss/templates/component.html.erb.tt
548
605
  - lib/rails/generators/test_unit/component_generator.rb