view_component 3.16.0 → 3.17.0

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: 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