primer_view_components 0.0.52 → 0.0.53

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: ad52227798e79cb0259c00e61d24a3e72854dfb4fa4ffb4901fb30a2fa7f3370
4
- data.tar.gz: 5f2e4decc740b1e9712744819d655091a2a45e9ac8b403beed54c8c9e6a6e332
3
+ metadata.gz: 4df5bb105c8b48aed71b4652afbf6814257fcb2c344085b328c27f8f109a80c1
4
+ data.tar.gz: 72ff93288cac5eb1cabdc7cb8031c488452499203d768fcf3795bbe18cb31a43
5
5
  SHA512:
6
- metadata.gz: c7cac3105837c025730e7c6e01395db680d1c750bf4d2240594f00c4758a7d404802d184dd3bd53a14c8a2e14e25776152792e97fceb2a686182aaf1474ae162
7
- data.tar.gz: f296ae7b5e8f6217b05e719169db5f08b1d1d8d7fb9293e083ae9a836f12d98c73e88cc320df3522a902f4dada28ac10ce7c9f37a96f0b175aaa1e2940e1bec9
6
+ metadata.gz: 3af26e886cc8998ef3d449471400319615c0e820ce7265316e6de6fda2b55340f7f7a82d3c7c78f5c4e393f60d7638126bd8e8a88675735bfe570d2958ee2484
7
+ data.tar.gz: aff9d0ca813126c33daab60f57bb01915ea8b779f17df99e648ec236f0da12bb9b16f54af24dfa871a50253663d71813533386457424f09f46a5458d5556a5b2
data/CHANGELOG.md CHANGED
@@ -30,6 +30,48 @@ The category for changes related to documentation, testing and tooling. Also, fo
30
30
 
31
31
  ## main
32
32
 
33
+ ## 0.0.53
34
+
35
+ ### New
36
+
37
+ * Add autocorrection to `FlashComponent` linter when the context is basic text.
38
+
39
+ *Manuel Puyol*
40
+
41
+ ### Updates
42
+
43
+ * Linters won't mark offenses when the ignore count is correct unless explicitly configured to do so.
44
+
45
+ *Manuel Puyol*
46
+
47
+ * Map the `for` argument when autofixing `ClipboardCopy` migrations.
48
+
49
+ *Kristján Oddsson*
50
+
51
+ * Add autocorrection for `CloseButton` linter.
52
+
53
+ *Manuel Puyol*
54
+
55
+ ### Bug fixes
56
+
57
+ * Linters won't convert HTML special elements.
58
+
59
+ *Manuel Puyol*
60
+
61
+ ### Misc
62
+
63
+ * Only run CHANGELOG CI on pull requests.
64
+
65
+ *Manuel Puyol*
66
+
67
+ * Run CI actions on pushes to main.
68
+
69
+ *Cameron Dutro*
70
+
71
+ * Get to 100% code coverage.
72
+
73
+ *Cameron Dutro*
74
+
33
75
  ## 0.0.52
34
76
 
35
77
  ### New
@@ -84,6 +126,10 @@ The category for changes related to documentation, testing and tooling. Also, fo
84
126
 
85
127
  *Manuel Puyol*
86
128
 
129
+ * Add a linter generator.
130
+
131
+ *Manuel Puyol*
132
+
87
133
  ## 0.0.51
88
134
 
89
135
  ### Breaking changes
@@ -40,7 +40,7 @@ module Primer
40
40
 
41
41
  # @example Default
42
42
  # <%= render(Primer::TabNavComponent.new(label: "Default")) do |c| %>
43
- # <% c.tab(selected: true, href: "#") { "Tab 1" }%>
43
+ # <% c.tab(selected: true, href: "#") { "Tab 1" } %>
44
44
  # <% c.tab(href: "#") { "Tab 2" } %>
45
45
  # <% c.tab(href: "#") { "Tab 3" } %>
46
46
  # <% end %>
@@ -212,8 +212,6 @@ module Primer
212
212
  else
213
213
  "color-shadow-#{val.to_s.dasherize}"
214
214
  end
215
- else
216
- memo[:classes] << "#{key.to_s.dasherize}#{breakpoint}-#{val.to_s.dasherize}"
217
215
  end
218
216
  end
219
217
 
@@ -29,7 +29,7 @@ module Primer
29
29
  private
30
30
 
31
31
  def format_hash(values, invert, symbolize)
32
- val = values.invert if invert
32
+ val = invert ? values.invert : values
33
33
  # remove defaults
34
34
  val = val.except("", nil)
35
35
 
@@ -51,7 +51,7 @@ module ERBLint
51
51
  erb_helper.raise_if_erb_block(classes_node)
52
52
 
53
53
  system_arguments = system_arguments_to_args(classes_node.value)
54
- args = classes_to_args(system_arguments[:classes])
54
+ args = classes_to_args(system_arguments[:classes]&.split || [])
55
55
 
56
56
  invalid_classes = args[:classes].select { |class_name| Primer::Classify::Validation.invalid?(class_name) }
57
57
 
@@ -77,7 +77,7 @@ module ERBLint
77
77
  # including a `classes` key that will contain all classes that the mapper couldn't handle.
78
78
  # @returns { classes: Array, ... }
79
79
  def classes_to_args(classes)
80
- { classes: classes&.split(" ") || [] }
80
+ { classes: classes }
81
81
  end
82
82
 
83
83
  def system_arguments_to_args(classes)
@@ -47,7 +47,7 @@ module ERBLint
47
47
  end
48
48
 
49
49
  def classes_to_args(classes)
50
- classes.split.each_with_object({ classes: [] }) do |class_name, acc|
50
+ classes.each_with_object({ classes: [] }) do |class_name, acc|
51
51
  next if class_name == "btn"
52
52
 
53
53
  if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
@@ -8,10 +8,12 @@ module ERBLint
8
8
  # Maps attributes in the clipboard-copy element to arguments for the ClipboardCopy component.
9
9
  class ClipboardCopy < Base
10
10
  DEFAULT_TAG = "clipboard-copy"
11
- ATTRIBUTES = %w[value].freeze
11
+ ATTRIBUTES = %w[role tabindex for value id style].freeze
12
12
 
13
13
  def attribute_to_args(attribute)
14
- { value: erb_helper.convert(attribute) }
14
+ attr_name = attribute.name
15
+
16
+ { attr_name.to_sym => erb_helper.convert(attribute) }
15
17
  end
16
18
  end
17
19
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module ArgumentMappers
8
+ # Maps classes in a close-button element to arguments for the CloseButton component.
9
+ class CloseButton < Base
10
+ ATTRIBUTES = %w[type].freeze
11
+
12
+ TYPE_OPTIONS = Primer::ViewComponents::Constants.get(
13
+ component: "Primer::CloseButton",
14
+ constant: "TYPE_OPTIONS"
15
+ ).freeze
16
+
17
+ DEFAULT_TYPE = Primer::ViewComponents::Constants.get(
18
+ component: "Primer::CloseButton",
19
+ constant: "DEFAULT_TYPE"
20
+ ).freeze
21
+
22
+ DEFAULT_CLASS = "close-button"
23
+
24
+ def attribute_to_args(attribute)
25
+ # button is the default type, so we don't need to do anything.
26
+ return {} if attribute.value == DEFAULT_TYPE
27
+
28
+ raise ConversionError, "CloseButton component does not support type \"#{attribute.value}\"" unless TYPE_OPTIONS.include?(attribute.value)
29
+
30
+ { type: ":#{attribute.value}" }
31
+ end
32
+
33
+ def classes_to_args(classes)
34
+ classes.each_with_object({ classes: [] }) do |class_name, acc|
35
+ next if class_name == DEFAULT_CLASS
36
+
37
+ acc[:classes] << class_name
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module ArgumentMappers
8
+ # Maps classes in a flash element to arguments for the Flash component.
9
+ class Flash < Base
10
+ SCHEME_MAPPINGS = Primer::ViewComponents::Constants.get(
11
+ component: "Primer::FlashComponent",
12
+ constant: "SCHEME_MAPPINGS",
13
+ symbolize: true
14
+ ).freeze
15
+
16
+ def classes_to_args(classes)
17
+ classes.each_with_object({ classes: [] }) do |class_name, acc|
18
+ next if class_name == "flash"
19
+
20
+ if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
21
+ acc[:scheme] = SCHEME_MAPPINGS[class_name]
22
+ elsif class_name == "flash-full"
23
+ acc[:full] = true
24
+ else
25
+ acc[:classes] << class_name
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -57,8 +57,8 @@ module ERBLint
57
57
  return m[:rb].strip
58
58
  end
59
59
 
60
- # wrap the result in `""` so it is printed as a string
61
- "\"#{attribute.value.gsub('<%=', '#{').gsub('%>', '}')}\""
60
+ # we use `source` instead of `value` because it does not convert encoded HTML entities.
61
+ attribute.value_node.loc.source.gsub("<%=", '#{').gsub("%>", "}")
62
62
  end
63
63
  end
64
64
  end
@@ -31,7 +31,7 @@ module ERBLint
31
31
  end
32
32
 
33
33
  def classes_to_args(classes)
34
- classes.split.each_with_object({ classes: [] }) do |class_name, acc|
34
+ classes.each_with_object({ classes: [] }) do |class_name, acc|
35
35
  next if class_name == "Label"
36
36
 
37
37
  if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
@@ -4,9 +4,11 @@ require_relative "argument_mappers/conversion_error"
4
4
 
5
5
  module ERBLint
6
6
  module Linters
7
- # Helper methods for autocorrectable ERB linters.
7
+ # Provides the autocorrection functionality for the linter. Once included, you should define the following constants:
8
+ # * `ARGUMENT_MAPPER` - required - The class responsible for transforming classes and attributes into arguments for the component.
9
+ # * `COMPONENT` - required - The component name for the linter. It will be used to generate the correction.
8
10
  module Autocorrectable
9
- def map_arguments(tag)
11
+ def map_arguments(tag, _tag_tree)
10
12
  self.class::ARGUMENT_MAPPER.new(tag).to_s
11
13
  rescue ArgumentMappers::ConversionError
12
14
  nil
@@ -4,10 +4,16 @@ require "json"
4
4
  require "openssl"
5
5
  require "primer/view_components/constants"
6
6
 
7
+ # :nocov:
8
+
7
9
  module ERBLint
8
10
  module Linters
9
- # Helper methods for linting ERB.
10
- module Helpers
11
+ # Provides the basic linter logic. When inherited, you should define:
12
+ # * `TAGS` - required - The HTML tags that the component supports. It will be used by the linter to match elements.
13
+ # * `MESSAGE` - required - The message shown when there's an offense.
14
+ # * `CLASSES` - optional - The CSS classes that the component needs. The linter will only match elements with one of those classes.
15
+ # * `REQUIRED_ARGUMENTS` - optional - A list of HTML attributes that are required by the component.
16
+ class BaseLinter < Linter
11
17
  # from https://github.com/Shopify/erb-lint/blob/6179ee2d9d681a6ec4dd02351a1e30eefa748d3d/lib/erb_lint/linters/self_closing_tag.rb
12
18
  SELF_CLOSING_TAGS = %w[
13
19
  area base br col command embed hr input keygen
@@ -15,18 +21,24 @@ module ERBLint
15
21
  ].freeze
16
22
 
17
23
  DUMP_FILE = ".erblint-counter-ignore.json"
24
+ DISALLOWED_CLASSES = [].freeze
18
25
  CLASSES = [].freeze
19
26
  REQUIRED_ARGUMENTS = [].freeze
20
27
 
21
- def self.included(base)
28
+ class ConfigSchema < LinterConfig
29
+ property :override_ignores_if_correctable, accepts: [true, false], default: false, reader: :override_ignores_if_correctable?
30
+ end
31
+
32
+ def self.inherited(base)
33
+ super
22
34
  base.include(ERBLint::LinterRegistry)
35
+ base.config_schema = ConfigSchema
23
36
  end
24
37
 
25
38
  def run(processed_source)
26
39
  @total_offenses = 0
27
40
  @offenses_not_corrected = 0
28
- tags = tags(processed_source)
29
- tag_tree = build_tag_tree(tags)
41
+ (tags, tag_tree) = build_tag_tree(processed_source)
30
42
 
31
43
  tags.each do |tag|
32
44
  next if tag.closing?
@@ -35,9 +47,10 @@ module ERBLint
35
47
  classes = tag.attributes["class"]&.value&.split(" ") || []
36
48
  tag_tree[tag][:offense] = false
37
49
 
50
+ next if (classes & self.class::DISALLOWED_CLASSES).any?
38
51
  next unless self.class::CLASSES.blank? || (classes & self.class::CLASSES).any?
39
52
 
40
- args = map_arguments(tag)
53
+ args = map_arguments(tag, tag_tree[tag])
41
54
  correction = correction(args)
42
55
 
43
56
  attributes = tag.attributes.each.map(&:name).join(" ")
@@ -55,8 +68,7 @@ module ERBLint
55
68
  @total_offenses += 1
56
69
  # We always fix the offenses using blocks. The closing tag corresponds to `<% end %>`.
57
70
  if h[:correctable]
58
- add_offense(tag.loc, h[:message], h[:correction])
59
- add_offense(h[:closing].loc, h[:message], "<% end %>")
71
+ add_correction(tag, h)
60
72
  else
61
73
  @offenses_not_corrected += 1
62
74
  generate_offense(self.class, processed_source, tag, h[:message])
@@ -82,11 +94,16 @@ module ERBLint
82
94
 
83
95
  private
84
96
 
97
+ def add_correction(tag, tag_tree)
98
+ add_offense(tag.loc, tag_tree[:message], tag_tree[:correction])
99
+ add_offense(tag_tree[:closing].loc, tag_tree[:message], "<% end %>")
100
+ end
101
+
85
102
  # Override this function to convert the HTML element attributes to argument for a component.
86
103
  #
87
104
  # @return [Hash] if possible to map all attributes to arguments.
88
105
  # @return [Nil] if cannot map to arguments.
89
- def map_arguments(_tag)
106
+ def map_arguments(_tag, _tag_tree)
90
107
  nil
91
108
  end
92
109
 
@@ -122,31 +139,44 @@ module ERBLint
122
139
  # This assumes that the AST provided represents valid HTML, where each tag has a corresponding closing tag.
123
140
  # From the tags, we build a structured tree which represents the tag hierarchy.
124
141
  # With this, we are able to know where the tags start and end.
125
- def build_tag_tree(tags)
142
+ def build_tag_tree(processed_source)
143
+ nodes = processed_source.ast.children
126
144
  tag_tree = {}
145
+ tags = []
127
146
  current_opened_tag = nil
128
147
 
129
- tags.each do |tag|
130
- if tag.closing?
131
- if current_opened_tag && tag.name == current_opened_tag.name
132
- tag_tree[current_opened_tag][:closing] = tag
133
- current_opened_tag = tag_tree[current_opened_tag][:parent]
134
- end
148
+ nodes.each do |node|
149
+ if node.type == :tag
150
+ # get the tag from previously calculated list so the references are the same
151
+ tag = BetterHtml::Tree::Tag.from_node(node)
152
+ tags << tag
135
153
 
136
- next
137
- end
154
+ if tag.closing?
155
+ if current_opened_tag && tag.name == current_opened_tag.name
156
+ tag_tree[current_opened_tag][:closing] = tag
157
+ current_opened_tag = tag_tree[current_opened_tag][:parent]
158
+ end
138
159
 
139
- self_closing = self_closing?(tag)
160
+ next
161
+ end
162
+
163
+ self_closing = self_closing?(tag)
140
164
 
141
- tag_tree[tag] = {
142
- closing: self_closing ? tag : nil,
143
- parent: current_opened_tag
144
- }
165
+ tag_tree[tag] = {
166
+ tag: tag,
167
+ closing: self_closing ? tag : nil,
168
+ parent: current_opened_tag,
169
+ children: []
170
+ }
145
171
 
146
- current_opened_tag = tag unless self_closing
172
+ tag_tree[current_opened_tag][:children] << tag_tree[tag] if current_opened_tag
173
+ current_opened_tag = tag unless self_closing
174
+ elsif current_opened_tag
175
+ tag_tree[current_opened_tag][:children] << node
176
+ end
147
177
  end
148
178
 
149
- tag_tree
179
+ [tags, tag_tree]
150
180
  end
151
181
 
152
182
  def self_closing?(tag)
@@ -173,6 +203,12 @@ module ERBLint
173
203
  end
174
204
  end
175
205
 
206
+ # Unless explicitly set, we don't want to mark correctable offenses if the counter is correct.
207
+ if !@config.override_ignores_if_correctable? && expected_count == @total_offenses
208
+ clear_offenses
209
+ return
210
+ end
211
+
176
212
  if @offenses_not_corrected.zero?
177
213
  # have to adjust to get `\n` so we delete the whole line
178
214
  add_offense(processed_source.to_source_range(comment_node.loc.adjust(end_pos: 1)), "Unused erblint:count comment for #{rule_name}", "") if comment_node
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "helpers"
3
+ require_relative "base_linter"
4
4
  require_relative "autocorrectable"
5
5
  require_relative "argument_mappers/button"
6
6
 
7
7
  module ERBLint
8
8
  module Linters
9
9
  # Counts the number of times a HTML button is used instead of the component.
10
- class ButtonComponentMigrationCounter < Linter
11
- include Helpers
10
+ class ButtonComponentMigrationCounter < BaseLinter
12
11
  include Autocorrectable
13
12
 
14
13
  TAGS = Primer::ViewComponents::Constants.get(
@@ -16,6 +15,8 @@ module ERBLint
16
15
  constant: "TAG_OPTIONS"
17
16
  ).freeze
18
17
 
18
+ # CloseButton component has preference when this class is seen in conjuction with `btn`.
19
+ DISALLOWED_CLASSES = %w[close-button].freeze
19
20
  CLASSES = %w[btn btn-link].freeze
20
21
  MESSAGE = "We are migrating buttons to use [Primer::ButtonComponent](https://primer.style/view-components/components/button), please try to use that instead of raw HTML."
21
22
  ARGUMENT_MAPPER = ArgumentMappers::Button
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "helpers"
3
+ require_relative "base_linter"
4
4
  require_relative "autocorrectable"
5
5
  require_relative "argument_mappers/clipboard_copy"
6
6
 
7
7
  module ERBLint
8
8
  module Linters
9
9
  # Counts the number of times a HTML clipboard-copy is used instead of the component.
10
- class ClipboardCopyComponentMigrationCounter < Linter
11
- include Helpers
10
+ class ClipboardCopyComponentMigrationCounter < BaseLinter
12
11
  include Autocorrectable
13
12
 
14
13
  TAGS = %w[clipboard-copy].freeze
@@ -1,16 +1,123 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "helpers"
3
+ require_relative "base_linter"
4
+ require_relative "autocorrectable"
5
+ require_relative "argument_mappers/close_button"
4
6
 
5
7
  module ERBLint
6
8
  module Linters
7
9
  # Counts the number of times a HTML clipboard-copy is used instead of the component.
8
- class CloseButtonComponentMigrationCounter < Linter
9
- include Helpers
10
+ class CloseButtonComponentMigrationCounter < BaseLinter
11
+ include Autocorrectable
10
12
 
11
13
  TAGS = %w[button].freeze
12
14
  CLASSES = %w[close-button].freeze
13
15
  MESSAGE = "We are migrating close-button to use [Primer::CloseButton](https://primer.style/view-components/components/closebutton), please try to use that instead of raw HTML."
16
+ ARGUMENT_MAPPER = ArgumentMappers::CloseButton
17
+ COMPONENT = "Primer::CloseButton"
18
+
19
+ ALLOWED_OCTICON_ARGS = %w[icon aria-label aria].freeze
20
+
21
+ private
22
+
23
+ def map_arguments(tag, tag_tree)
24
+ # We can only autocorrect cases where the tag only has an octicon as content.
25
+ return if tag_tree[:children].size != 1
26
+
27
+ nodes = tag_tree[:children].first.children
28
+ erb_nodes = nodes.select { |node| node.try(:type) == :erb }
29
+
30
+ # Don't correct if there are multiple ERB nodes.
31
+ return if erb_nodes.size != 1
32
+
33
+ _, _, code_node = *erb_nodes.first
34
+ code = code_node.children.first.strip
35
+ ast = erb_ast(code)
36
+
37
+ # We'll only autocorrect cases where the only content is an octicon.
38
+ if ast.method_name == :primer_octicon || ast.method_name == :octicon
39
+ octicon_kwargs = ast.arguments[1]
40
+ icon = icon(ast.arguments)
41
+ elsif ast.method_name == :render && code.include?("Primer::OcticonComponent")
42
+ octicon_kwargs = ast.arguments.first.arguments.last
43
+ icon = icon(ast.arguments.first.arguments)
44
+ else
45
+ return
46
+ end
47
+
48
+ # Don't autocorrect if using a custom icon
49
+ return unless icon == :x
50
+ # Don't autocorrect if the octicon has custom arguments
51
+ return if custom_attributes?(octicon_kwargs)
52
+
53
+ octicon_aria_label = aria_label_from_octicon(octicon_kwargs)
54
+ tag_aria_label = tag.attributes.each.find { |a| a.name == "aria-label" }
55
+
56
+ # Can't autocorrect if there is no aria-label.
57
+ return if octicon_aria_label.blank? && tag_aria_label.blank?
58
+
59
+ args = ARGUMENT_MAPPER.new(tag).to_s
60
+
61
+ # Argument mapper will add the `aria-label` if the tag has it.
62
+ return args if tag_aria_label.present?
63
+
64
+ aria_label_arg = "\"aria-label\": #{octicon_aria_label}"
65
+
66
+ return aria_label_arg if args.blank?
67
+
68
+ "#{args}, #{aria_label_arg}"
69
+ rescue ArgumentMappers::ConversionError
70
+ nil
71
+ end
72
+
73
+ # Overriding the basic correction since this component does not uses content blocks.
74
+ def correction(args)
75
+ return if args.nil?
76
+
77
+ correction = "<%= render #{self.class::COMPONENT}.new"
78
+ correction += "(#{args})" if args.present?
79
+ "#{correction} %>"
80
+ end
81
+
82
+ # Overriding the basic correction since this component will rewrite the whole tag block.
83
+ def add_correction(tag, tag_tree)
84
+ offense_loc = tag.loc.with(end_pos: tag_tree[:closing].loc.to_range.last)
85
+ add_offense(offense_loc, tag_tree[:message], tag_tree[:correction])
86
+ end
87
+
88
+ # Extracts the aria-label value from the octicon kwargs.
89
+ # It can either be in `"aria-label": "value"`` or `aria: { label: "value" } }`.
90
+ def aria_label_from_octicon(kwargs)
91
+ return if kwargs.blank? || kwargs.type != :hash || kwargs.pairs.blank?
92
+
93
+ aria_label = kwargs.pairs.find { |x| x.key.value == :"aria-label" }
94
+
95
+ return aria_label.value.source if aria_label
96
+
97
+ aria_hash = kwargs.pairs.find { |x| x.key.value == :aria }
98
+
99
+ return if aria_hash.blank?
100
+
101
+ aria_label = aria_hash.value.pairs.find { |x| x.key.value == :label }
102
+
103
+ aria_label&.value&.source
104
+ end
105
+
106
+ def custom_attributes?(kwargs)
107
+ return false if kwargs.blank? || kwargs.type != :hash || kwargs.pairs.blank?
108
+
109
+ (kwargs.keys.map { |key| key.value.to_s } - ALLOWED_OCTICON_ARGS).present?
110
+ end
111
+
112
+ def erb_ast(code)
113
+ RuboCop::AST::ProcessedSource.new(code, RUBY_VERSION.to_f).ast
114
+ end
115
+
116
+ def icon(args)
117
+ return args.first.value.to_sym if args.first.type == :sym || args.first.type == :str
118
+
119
+ args.last.pairs.find { |x| x.key.value == :icon }.value.value.to_sym
120
+ end
14
121
  end
15
122
  end
16
123
  end
@@ -1,16 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "helpers"
3
+ require_relative "base_linter"
4
+ require_relative "autocorrectable"
5
+ require_relative "argument_mappers/flash"
4
6
 
5
7
  module ERBLint
6
8
  module Linters
7
9
  # Counts the number of times a HTML flash is used instead of the component.
8
- class FlashComponentMigrationCounter < Linter
9
- include Helpers
10
+ class FlashComponentMigrationCounter < BaseLinter
11
+ include Autocorrectable
10
12
 
11
13
  TAGS = %w[div].freeze
12
14
  CLASSES = %w[flash].freeze
13
15
  MESSAGE = "We are migrating flashes to use [Primer::FlashComponent](https://primer.style/view-components/components/flash), please try to use that instead of raw HTML."
16
+ ARGUMENT_MAPPER = ArgumentMappers::Flash
17
+ COMPONENT = "Primer::FlashComponent"
18
+
19
+ def map_arguments(tag, tag_tree)
20
+ # We can only autocorrect elements with simple text as content.
21
+ return nil if tag_tree[:children].size != 1
22
+ # Hash children indicates that there are tags in the content.
23
+ return nil if tag_tree[:children].first.is_a?(Hash)
24
+
25
+ ARGUMENT_MAPPER.new(tag).to_s
26
+ rescue ArgumentMappers::ConversionError
27
+ nil
28
+ end
14
29
  end
15
30
  end
16
31
  end
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "helpers"
3
+ require_relative "base_linter"
4
4
  require_relative "autocorrectable"
5
5
  require_relative "argument_mappers/label"
6
6
 
7
7
  module ERBLint
8
8
  module Linters
9
9
  # Counts the number of times a HTML label is used instead of the component.
10
- class LabelComponentMigrationCounter < Linter
11
- include Helpers
10
+ class LabelComponentMigrationCounter < BaseLinter
12
11
  include Autocorrectable
13
12
 
14
13
  TAGS = Primer::ViewComponents::Constants.get(
@@ -5,7 +5,7 @@ module Primer
5
5
  module VERSION
6
6
  MAJOR = 0
7
7
  MINOR = 0
8
- PATCH = 52
8
+ PATCH = 53
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH].join(".")
11
11
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :nocov:
4
+
3
5
  module YARD
4
6
  # Helper methods to use for yard documentation
5
7
  module DocsHelper
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: primer_view_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.52
4
+ version: 0.0.53
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-18 00:00:00.000000000 Z
11
+ date: 2021-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -324,6 +324,20 @@ dependencies:
324
324
  - - ">="
325
325
  - !ruby/object:Gem::Version
326
326
  version: '0'
327
+ - !ruby/object:Gem::Dependency
328
+ name: timecop
329
+ requirement: !ruby/object:Gem::Requirement
330
+ requirements:
331
+ - - ">="
332
+ - !ruby/object:Gem::Version
333
+ version: '0'
334
+ type: :development
335
+ prerelease: false
336
+ version_requirements: !ruby/object:Gem::Requirement
337
+ requirements:
338
+ - - ">="
339
+ - !ruby/object:Gem::Version
340
+ version: '0'
327
341
  - !ruby/object:Gem::Dependency
328
342
  name: yard
329
343
  requirement: !ruby/object:Gem::Requirement
@@ -486,16 +500,18 @@ files:
486
500
  - lib/primer/view_components/linters/argument_mappers/base.rb
487
501
  - lib/primer/view_components/linters/argument_mappers/button.rb
488
502
  - lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb
503
+ - lib/primer/view_components/linters/argument_mappers/close_button.rb
489
504
  - lib/primer/view_components/linters/argument_mappers/conversion_error.rb
505
+ - lib/primer/view_components/linters/argument_mappers/flash.rb
490
506
  - lib/primer/view_components/linters/argument_mappers/helpers/erb_block.rb
491
507
  - lib/primer/view_components/linters/argument_mappers/label.rb
492
508
  - lib/primer/view_components/linters/argument_mappers/system_arguments.rb
493
509
  - lib/primer/view_components/linters/autocorrectable.rb
510
+ - lib/primer/view_components/linters/base_linter.rb
494
511
  - lib/primer/view_components/linters/button_component_migration_counter.rb
495
512
  - lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb
496
513
  - lib/primer/view_components/linters/close_button_component_migration_counter.rb
497
514
  - lib/primer/view_components/linters/flash_component_migration_counter.rb
498
- - lib/primer/view_components/linters/helpers.rb
499
515
  - lib/primer/view_components/linters/label_component_migration_counter.rb
500
516
  - lib/primer/view_components/statuses.rb
501
517
  - lib/primer/view_components/version.rb