primer_view_components 0.0.51 → 0.0.52
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 +4 -4
- data/CHANGELOG.md +60 -0
- data/app/components/primer/beta/avatar_stack.rb +9 -9
- data/app/components/primer/beta/truncate.html.erb +5 -0
- data/app/components/primer/beta/truncate.rb +110 -0
- data/app/components/primer/border_box_component.rb +27 -1
- data/app/components/primer/clipboard_copy.rb +1 -1
- data/app/components/primer/dropdown.rb +7 -7
- data/app/components/primer/icon_button.rb +1 -1
- data/app/components/primer/navigation/tab_component.rb +1 -1
- data/app/components/primer/progress_bar_component.rb +0 -3
- data/app/lib/primer/fetch_or_fallback_helper.rb +2 -0
- data/app/lib/primer/tabbed_component_helper.rb +1 -1
- data/app/lib/primer/view_helper.rb +1 -0
- data/lib/primer/classify.rb +4 -6
- data/lib/primer/classify/flex.rb +1 -1
- data/lib/primer/classify/functional_colors.rb +1 -1
- data/lib/primer/classify/utilities.rb +16 -1
- data/lib/primer/classify/validation.rb +18 -0
- data/lib/primer/view_components/linters/argument_mappers/base.rb +33 -7
- data/lib/primer/view_components/linters/argument_mappers/button.rb +5 -6
- data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +1 -2
- data/lib/primer/view_components/linters/argument_mappers/helpers/erb_block.rb +48 -5
- data/lib/primer/view_components/linters/argument_mappers/label.rb +3 -4
- data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +5 -7
- data/lib/primer/view_components/linters/autocorrectable.rb +2 -2
- data/lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb +1 -1
- data/lib/primer/view_components/linters/helpers.rb +8 -4
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/rubocop/config/default.yml +5 -0
- data/lib/rubocop/cop/primer.rb +1 -2
- data/lib/rubocop/cop/primer/deprecated_arguments.rb +173 -0
- data/lib/rubocop/cop/primer/no_tag_memoize.rb +1 -0
- data/lib/rubocop/cop/primer/primer_octicon.rb +178 -0
- data/lib/rubocop/cop/primer/system_argument_instead_of_class.rb +3 -17
- data/lib/tasks/coverage.rake +4 -0
- data/lib/tasks/docs.rake +3 -2
- data/lib/tasks/utilities.rake +5 -3
- data/lib/yard/docs_helper.rb +4 -3
- data/static/arguments.yml +7 -0
- data/static/classes.yml +8 -0
- data/static/constants.json +13 -1
- data/static/statuses.json +3 -1
- metadata +9 -4
data/lib/primer/classify/flex.rb
CHANGED
@@ -89,7 +89,7 @@ module Primer
|
|
89
89
|
def justify_content(value, breakpoint)
|
90
90
|
val = fetch_or_fallback(JUSTIFY_CONTENT_VALUES, value)
|
91
91
|
|
92
|
-
formatted_value = val.to_s.gsub(/(
|
92
|
+
formatted_value = val.to_s.gsub(/(flex_|space_)/, "")
|
93
93
|
"flex#{breakpoint}-justify-#{formatted_value}"
|
94
94
|
end
|
95
95
|
|
@@ -85,7 +85,7 @@ module Primer
|
|
85
85
|
return { classes: classes } if ENV["RAILS_ENV"] == "production"
|
86
86
|
|
87
87
|
obj = {}
|
88
|
-
classes = classes.split
|
88
|
+
classes = classes.split
|
89
89
|
# Loop through all classes supplied and reject ones we find a match for
|
90
90
|
# So when we're at the end of the loop we have classes left with any non-system classes.
|
91
91
|
classes.reject! do |classname|
|
@@ -114,6 +114,21 @@ module Primer
|
|
114
114
|
obj
|
115
115
|
end
|
116
116
|
|
117
|
+
def classes_to_args(classes)
|
118
|
+
classes_to_hash(classes).map do |key, value|
|
119
|
+
val = case value
|
120
|
+
when Symbol
|
121
|
+
":#{value}"
|
122
|
+
when String
|
123
|
+
value.to_json
|
124
|
+
else
|
125
|
+
value
|
126
|
+
end
|
127
|
+
|
128
|
+
"#{key}: #{val}"
|
129
|
+
end.join(", ")
|
130
|
+
end
|
131
|
+
|
117
132
|
private
|
118
133
|
|
119
134
|
def find_selector(selector)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "utilities"
|
4
|
+
|
5
|
+
module Primer
|
6
|
+
class Classify
|
7
|
+
# :nodoc:
|
8
|
+
class Validation
|
9
|
+
INVALID_CLASS_NAME_PREFIXES = /bg-|color-|text-|box-shadow-|text-|box_shadow-/.freeze
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def invalid?(class_name)
|
13
|
+
class_name.start_with?(INVALID_CLASS_NAME_PREFIXES) || Primer::Classify::Utilities.supported_selector?(class_name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "primer/view_components/constants"
|
4
4
|
require "primer/classify/utilities"
|
5
|
+
require "primer/classify/validation"
|
5
6
|
require_relative "conversion_error"
|
6
7
|
require_relative "system_arguments"
|
7
8
|
require_relative "helpers/erb_block"
|
@@ -46,18 +47,37 @@ module ERBLint
|
|
46
47
|
|
47
48
|
def attribute_to_args(attribute); end
|
48
49
|
|
49
|
-
def map_classes(
|
50
|
-
|
50
|
+
def map_classes(classes_node)
|
51
|
+
erb_helper.raise_if_erb_block(classes_node)
|
52
|
+
|
53
|
+
system_arguments = system_arguments_to_args(classes_node.value)
|
51
54
|
args = classes_to_args(system_arguments[:classes])
|
52
55
|
|
53
|
-
args.
|
56
|
+
invalid_classes = args[:classes].select { |class_name| Primer::Classify::Validation.invalid?(class_name) }
|
57
|
+
|
58
|
+
raise ConversionError, "Cannot convert #{'class'.pluralize(invalid_classes.size)} #{invalid_classes.join(',')}" if invalid_classes.present?
|
59
|
+
|
60
|
+
# Using splat to order the arguments in Component's args -> System Args -> custom classes
|
61
|
+
res = {
|
62
|
+
**args.except(:classes),
|
63
|
+
**system_arguments.except(:classes)
|
64
|
+
}
|
65
|
+
|
66
|
+
if args[:classes].present?
|
67
|
+
res = {
|
68
|
+
**res,
|
69
|
+
classes: args[:classes].join(" ").to_json
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
res
|
54
74
|
end
|
55
75
|
|
56
|
-
# Override this with your component's mappings
|
76
|
+
# Override this with your component's mappings, it should return a hash with the component's arguments,
|
77
|
+
# including a `classes` key that will contain all classes that the mapper couldn't handle.
|
78
|
+
# @returns { classes: Array, ... }
|
57
79
|
def classes_to_args(classes)
|
58
|
-
|
59
|
-
|
60
|
-
{}
|
80
|
+
{ classes: classes&.split(" ") || [] }
|
61
81
|
end
|
62
82
|
|
63
83
|
def system_arguments_to_args(classes)
|
@@ -68,6 +88,12 @@ module ERBLint
|
|
68
88
|
v.is_a?(Symbol) ? ":#{v}" : v
|
69
89
|
end
|
70
90
|
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def erb_helper
|
95
|
+
@erb_helper ||= Helpers::ErbBlock.new
|
96
|
+
end
|
71
97
|
end
|
72
98
|
end
|
73
99
|
end
|
@@ -7,8 +7,6 @@ module ERBLint
|
|
7
7
|
module ArgumentMappers
|
8
8
|
# Maps classes in a button element to arguments for the Button component.
|
9
9
|
class Button < Base
|
10
|
-
require "pry"
|
11
|
-
|
12
10
|
SCHEME_MAPPINGS = Primer::ViewComponents::Constants.get(
|
13
11
|
component: "Primer::ButtonComponent",
|
14
12
|
constant: "SCHEME_MAPPINGS",
|
@@ -35,9 +33,10 @@ module ERBLint
|
|
35
33
|
def attribute_to_args(attribute)
|
36
34
|
attr_name = attribute.name
|
37
35
|
|
38
|
-
|
36
|
+
case attr_name
|
37
|
+
when "disabled"
|
39
38
|
{ disabled: true }
|
40
|
-
|
39
|
+
when "type"
|
41
40
|
# button is the default type, so we don't need to do anything.
|
42
41
|
return {} if attribute.value == "button"
|
43
42
|
|
@@ -48,7 +47,7 @@ module ERBLint
|
|
48
47
|
end
|
49
48
|
|
50
49
|
def classes_to_args(classes)
|
51
|
-
classes.split
|
50
|
+
classes.split.each_with_object({ classes: [] }) do |class_name, acc|
|
52
51
|
next if class_name == "btn"
|
53
52
|
|
54
53
|
if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
|
@@ -60,7 +59,7 @@ module ERBLint
|
|
60
59
|
elsif class_name == "BtnGroup-item"
|
61
60
|
acc[:group_item] = true
|
62
61
|
else
|
63
|
-
|
62
|
+
acc[:classes] << class_name
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
@@ -8,15 +8,58 @@ module ERBLint
|
|
8
8
|
module Helpers
|
9
9
|
# provides helpers to identify and deal with ERB blocks.
|
10
10
|
class ErbBlock
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
INTERPOLATION_REGEX = /^<%=(?<rb>.*)%>$/.freeze
|
12
|
+
|
13
|
+
def raise_if_erb_block(attribute)
|
14
|
+
raise_error(attribute) if any?(attribute)
|
15
|
+
end
|
16
|
+
|
17
|
+
def convert(attribute)
|
18
|
+
raise_error(attribute) unless interpolation?(attribute)
|
19
|
+
|
20
|
+
if any?(attribute)
|
21
|
+
convert_interpolation(attribute)
|
22
|
+
else
|
23
|
+
attribute.value.to_json
|
14
24
|
end
|
25
|
+
end
|
15
26
|
|
16
|
-
|
17
|
-
|
27
|
+
private
|
28
|
+
|
29
|
+
def interpolation?(attribute)
|
30
|
+
erb_blocks(attribute).all? do |erb|
|
31
|
+
# If the blocks does not have an indicator, it's not an interpolation.
|
32
|
+
erb.children.to_a.compact.any? { |node| node.type == :indicator }
|
18
33
|
end
|
19
34
|
end
|
35
|
+
|
36
|
+
def raise_error(attribute)
|
37
|
+
raise ERBLint::Linters::ArgumentMappers::ConversionError, "Cannot convert attribute \"#{attribute.name}\" because its value contains an erb block"
|
38
|
+
end
|
39
|
+
|
40
|
+
def any?(attribute)
|
41
|
+
erb_blocks(attribute).any?
|
42
|
+
end
|
43
|
+
|
44
|
+
def basic?(attribute)
|
45
|
+
return false if erb_blocks(attribute).size != 1
|
46
|
+
|
47
|
+
attribute.value.match?(INTERPOLATION_REGEX)
|
48
|
+
end
|
49
|
+
|
50
|
+
def erb_blocks(attribute)
|
51
|
+
(attribute.value_node&.children || []).select { |n| n.try(:type) == :erb }
|
52
|
+
end
|
53
|
+
|
54
|
+
def convert_interpolation(attribute)
|
55
|
+
if basic?(attribute)
|
56
|
+
m = attribute.value.match(INTERPOLATION_REGEX)
|
57
|
+
return m[:rb].strip
|
58
|
+
end
|
59
|
+
|
60
|
+
# wrap the result in `""` so it is printed as a string
|
61
|
+
"\"#{attribute.value.gsub('<%=', '#{').gsub('%>', '}')}\""
|
62
|
+
end
|
20
63
|
end
|
21
64
|
end
|
22
65
|
end
|
@@ -27,12 +27,11 @@ module ERBLint
|
|
27
27
|
ATTRIBUTES = %w[title].freeze
|
28
28
|
|
29
29
|
def attribute_to_args(attribute)
|
30
|
-
|
31
|
-
{ title: attribute.value.to_json }
|
30
|
+
{ title: erb_helper.convert(attribute) }
|
32
31
|
end
|
33
32
|
|
34
33
|
def classes_to_args(classes)
|
35
|
-
classes.split
|
34
|
+
classes.split.each_with_object({ classes: [] }) do |class_name, acc|
|
36
35
|
next if class_name == "Label"
|
37
36
|
|
38
37
|
if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
|
@@ -40,7 +39,7 @@ module ERBLint
|
|
40
39
|
elsif VARIANT_MAPPINGS[class_name] && acc[:variant].nil?
|
41
40
|
acc[:variant] = VARIANT_MAPPINGS[class_name]
|
42
41
|
else
|
43
|
-
|
42
|
+
acc[:classes] << class_name
|
44
43
|
end
|
45
44
|
end
|
46
45
|
end
|
@@ -11,9 +11,11 @@ module ERBLint
|
|
11
11
|
STRING_PARAMETERS = %w[aria- data-].freeze
|
12
12
|
TEST_SELECTOR_REGEX = /test_selector\((?<selector>.+)\)$/.freeze
|
13
13
|
|
14
|
-
attr_reader :attribute
|
14
|
+
attr_reader :attribute, :erb_helper
|
15
|
+
|
15
16
|
def initialize(attribute)
|
16
17
|
@attribute = attribute
|
18
|
+
@erb_helper = Helpers::ErbBlock.new
|
17
19
|
end
|
18
20
|
|
19
21
|
def to_args
|
@@ -29,13 +31,9 @@ module ERBLint
|
|
29
31
|
|
30
32
|
{ test_selector: m[:selector].tr("'", '"') }
|
31
33
|
elsif attr_name == "data-test-selector"
|
32
|
-
|
33
|
-
|
34
|
-
{ test_selector: attribute.value.to_json }
|
34
|
+
{ test_selector: erb_helper.convert(attribute) }
|
35
35
|
elsif attr_name.start_with?(*STRING_PARAMETERS)
|
36
|
-
|
37
|
-
|
38
|
-
{ "\"#{attr_name}\"" => attribute.value.to_json }
|
36
|
+
{ "\"#{attr_name}\"" => erb_helper.convert(attribute) }
|
39
37
|
else
|
40
38
|
raise ConversionError, "Cannot convert attribute \"#{attr_name}\""
|
41
39
|
end
|
@@ -20,10 +20,10 @@ module ERBLint
|
|
20
20
|
"#{correction} do %>"
|
21
21
|
end
|
22
22
|
|
23
|
-
def message(args)
|
23
|
+
def message(args, processed_source)
|
24
24
|
return self.class::MESSAGE if args.nil?
|
25
25
|
|
26
|
-
"#{self.class::MESSAGE}\
|
26
|
+
"#{self.class::MESSAGE}\nTry using:\n\n#{correction(args)}\n\nYou can also run erblint in autocorrect mode:\n\nbundle exec erblint -a #{processed_source.filename}\n"
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -12,7 +12,7 @@ module ERBLint
|
|
12
12
|
include Autocorrectable
|
13
13
|
|
14
14
|
TAGS = %w[clipboard-copy].freeze
|
15
|
-
|
15
|
+
REQUIRED_ARGUMENTS = [/for|value/, "aria-label"].freeze
|
16
16
|
MESSAGE = "We are migrating clipboard-copy to use [Primer::ClipboardCopy](https://primer.style/view-components/components/clipboardcopy), please try to use that instead of raw HTML."
|
17
17
|
ARGUMENT_MAPPER = ArgumentMappers::ClipboardCopy
|
18
18
|
COMPONENT = "Primer::ClipboardCopy"
|
@@ -15,6 +15,8 @@ module ERBLint
|
|
15
15
|
].freeze
|
16
16
|
|
17
17
|
DUMP_FILE = ".erblint-counter-ignore.json"
|
18
|
+
CLASSES = [].freeze
|
19
|
+
REQUIRED_ARGUMENTS = [].freeze
|
18
20
|
|
19
21
|
def self.included(base)
|
20
22
|
base.include(ERBLint::LinterRegistry)
|
@@ -31,7 +33,6 @@ module ERBLint
|
|
31
33
|
next unless self.class::TAGS&.include?(tag.name)
|
32
34
|
|
33
35
|
classes = tag.attributes["class"]&.value&.split(" ") || []
|
34
|
-
|
35
36
|
tag_tree[tag][:offense] = false
|
36
37
|
|
37
38
|
next unless self.class::CLASSES.blank? || (classes & self.class::CLASSES).any?
|
@@ -39,9 +40,12 @@ module ERBLint
|
|
39
40
|
args = map_arguments(tag)
|
40
41
|
correction = correction(args)
|
41
42
|
|
43
|
+
attributes = tag.attributes.each.map(&:name).join(" ")
|
44
|
+
matches_required_attributes = self.class::REQUIRED_ARGUMENTS.blank? || self.class::REQUIRED_ARGUMENTS.all? { |arg| attributes.match?(arg) }
|
45
|
+
|
42
46
|
tag_tree[tag][:offense] = true
|
43
|
-
tag_tree[tag][:correctable] = !correction.nil?
|
44
|
-
tag_tree[tag][:message] = message(args)
|
47
|
+
tag_tree[tag][:correctable] = matches_required_attributes && !correction.nil?
|
48
|
+
tag_tree[tag][:message] = message(args, processed_source)
|
45
49
|
tag_tree[tag][:correction] = correction
|
46
50
|
end
|
47
51
|
|
@@ -97,7 +101,7 @@ module ERBLint
|
|
97
101
|
# Override this function to customize the linter message.
|
98
102
|
#
|
99
103
|
# @return [String] message to show on linter error.
|
100
|
-
def message(_tag)
|
104
|
+
def message(_tag, _processed_source)
|
101
105
|
self.class::MESSAGE
|
102
106
|
end
|
103
107
|
|
data/lib/rubocop/cop/primer.rb
CHANGED
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubocop"
|
4
|
+
require "primer/view_components/statuses"
|
5
|
+
require_relative "../../../../app/lib/primer/view_helper"
|
6
|
+
|
7
|
+
# :nocov:
|
8
|
+
module RuboCop
|
9
|
+
module Cop
|
10
|
+
module Primer
|
11
|
+
# This cop ensures that components don't use deprecated arguments
|
12
|
+
#
|
13
|
+
# bad
|
14
|
+
# Component.new(foo: :deprecated)
|
15
|
+
#
|
16
|
+
# good
|
17
|
+
# Component.new(foo: :bar)
|
18
|
+
class DeprecatedArguments < RuboCop::Cop::Cop
|
19
|
+
INVALID_MESSAGE = <<~STR
|
20
|
+
Avoid using deprecated arguments: https://primer.style/view-components/deprecated.
|
21
|
+
STR
|
22
|
+
|
23
|
+
# This is a hash of deprecated arguments and their replacements.
|
24
|
+
#
|
25
|
+
# * The top level key is the argument.
|
26
|
+
# * The second level key is the value.
|
27
|
+
# * The seceond level value is a string of the full replacement. e.g. "new_argument: :new_value"
|
28
|
+
# If the value is nil, then there is no replacement.
|
29
|
+
#
|
30
|
+
# e.g.
|
31
|
+
# DEPRECATED = {
|
32
|
+
# argument: {
|
33
|
+
# value: "new_argument: :new_value"
|
34
|
+
# }
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
DEPRECATED = {
|
38
|
+
color: {
|
39
|
+
blue: "color: :text_link",
|
40
|
+
gray_dark: "color: :text_primary",
|
41
|
+
gray: "color: :text_secondary",
|
42
|
+
gray_light: "color: :text_tertiary",
|
43
|
+
green: "color: :text_success",
|
44
|
+
yellow: "color: :text_warning",
|
45
|
+
red: "color: :text_danger",
|
46
|
+
gray_0: nil,
|
47
|
+
gray_1: nil,
|
48
|
+
gray_2: nil,
|
49
|
+
gray_3: nil,
|
50
|
+
gray_4: nil,
|
51
|
+
gray_5: nil,
|
52
|
+
gray_6: nil,
|
53
|
+
gray_7: nil,
|
54
|
+
gray_8: nil,
|
55
|
+
gray_9: nil,
|
56
|
+
blue_0: nil,
|
57
|
+
blue_1: nil,
|
58
|
+
blue_2: nil,
|
59
|
+
blue_3: nil,
|
60
|
+
blue_4: nil,
|
61
|
+
blue_5: nil,
|
62
|
+
blue_6: nil,
|
63
|
+
blue_7: nil,
|
64
|
+
blue_8: nil,
|
65
|
+
blue_9: nil,
|
66
|
+
green_0: nil,
|
67
|
+
green_1: nil,
|
68
|
+
green_2: nil,
|
69
|
+
green_3: nil,
|
70
|
+
green_4: nil,
|
71
|
+
green_5: nil,
|
72
|
+
green_6: nil,
|
73
|
+
green_7: nil,
|
74
|
+
green_8: nil,
|
75
|
+
green_9: nil,
|
76
|
+
yellow_0: nil,
|
77
|
+
yellow_1: nil,
|
78
|
+
yellow_2: nil,
|
79
|
+
yellow_3: nil,
|
80
|
+
yellow_4: nil,
|
81
|
+
yellow_5: nil,
|
82
|
+
yellow_6: nil,
|
83
|
+
yellow_7: nil,
|
84
|
+
yellow_8: nil,
|
85
|
+
yellow_9: nil,
|
86
|
+
red_0: nil,
|
87
|
+
red_1: nil,
|
88
|
+
red_2: nil,
|
89
|
+
red_3: nil,
|
90
|
+
red_4: nil,
|
91
|
+
red_5: nil,
|
92
|
+
red_6: nil,
|
93
|
+
red_7: nil,
|
94
|
+
red_8: nil,
|
95
|
+
red_9: nil,
|
96
|
+
purple_0: nil,
|
97
|
+
purple_1: nil,
|
98
|
+
purple_2: nil,
|
99
|
+
purple_3: nil,
|
100
|
+
purple_4: nil,
|
101
|
+
purple_5: nil,
|
102
|
+
purple_6: nil,
|
103
|
+
purple_7: nil,
|
104
|
+
purple_8: nil,
|
105
|
+
purple_9: nil,
|
106
|
+
pink_0: nil,
|
107
|
+
pink_1: nil,
|
108
|
+
pink_2: nil,
|
109
|
+
pink_3: nil,
|
110
|
+
pink_4: nil,
|
111
|
+
pink_5: nil,
|
112
|
+
pink_6: nil,
|
113
|
+
pink_7: nil,
|
114
|
+
pink_8: nil,
|
115
|
+
pink_9: nil,
|
116
|
+
orange_0: nil,
|
117
|
+
orange_1: nil,
|
118
|
+
orange_2: nil,
|
119
|
+
orange_3: nil,
|
120
|
+
orange_4: nil,
|
121
|
+
orange_5: nil,
|
122
|
+
orange_6: nil,
|
123
|
+
orange_7: nil,
|
124
|
+
orange_8: nil,
|
125
|
+
orange_9: nil
|
126
|
+
}
|
127
|
+
}.freeze
|
128
|
+
|
129
|
+
def on_send(node)
|
130
|
+
return unless valid_node?(node)
|
131
|
+
return unless node.arguments?
|
132
|
+
|
133
|
+
# we are looking for hash arguments and they are always last
|
134
|
+
kwargs = node.arguments.last
|
135
|
+
|
136
|
+
return unless kwargs.type == :hash
|
137
|
+
|
138
|
+
kwargs.pairs.each do |pair|
|
139
|
+
# Skip if we're not dealing with a symbol
|
140
|
+
next if pair.key.type != :sym
|
141
|
+
next unless pair.value.type == :sym || pair.value.type == :str
|
142
|
+
|
143
|
+
key = pair.key.value
|
144
|
+
value = pair.value.value.to_sym
|
145
|
+
|
146
|
+
next unless DEPRECATED.key?(key) && DEPRECATED[key].key?(value)
|
147
|
+
|
148
|
+
add_offense(pair, message: INVALID_MESSAGE)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def autocorrect(node)
|
153
|
+
lambda do |corrector|
|
154
|
+
replacement = DEPRECATED[node.key.value][node.value.value.to_sym]
|
155
|
+
corrector.replace(node, replacement) if replacement.present?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
# We only verify SystemArguments if it's a `.new` call on a component or
|
162
|
+
# a ViewHleper call.
|
163
|
+
def valid_node?(node)
|
164
|
+
view_helpers.include?(node.method_name) || (node.method_name == :new && ::Primer::ViewComponents::STATUSES.key?(node.receiver.const_name))
|
165
|
+
end
|
166
|
+
|
167
|
+
def view_helpers
|
168
|
+
::Primer::ViewHelper::HELPERS.keys.map { |key| "primer_#{key}".to_sym }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|