theme-check 0.7.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/theme-check.yml +1 -0
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +33 -0
  5. data/RELEASING.md +5 -3
  6. data/config/default.yml +1 -1
  7. data/data/shopify_liquid/tags.yml +3 -0
  8. data/data/shopify_translation_keys.yml +1 -0
  9. data/dev.yml +1 -1
  10. data/docs/checks/nested_snippet.md +1 -1
  11. data/docs/checks/space_inside_braces.md +28 -0
  12. data/exe/theme-check +1 -1
  13. data/lib/theme_check.rb +5 -0
  14. data/lib/theme_check/analyzer.rb +19 -9
  15. data/lib/theme_check/bug.rb +20 -0
  16. data/lib/theme_check/check.rb +5 -1
  17. data/lib/theme_check/checks.rb +39 -8
  18. data/lib/theme_check/checks/missing_enable_comment.rb +4 -4
  19. data/lib/theme_check/checks/nested_snippet.rb +1 -1
  20. data/lib/theme_check/checks/space_inside_braces.rb +8 -2
  21. data/lib/theme_check/cli.rb +99 -64
  22. data/lib/theme_check/config.rb +6 -2
  23. data/lib/theme_check/disabled_check.rb +39 -0
  24. data/lib/theme_check/disabled_checks.rb +20 -32
  25. data/lib/theme_check/exceptions.rb +32 -0
  26. data/lib/theme_check/json_file.rb +5 -1
  27. data/lib/theme_check/language_server.rb +1 -1
  28. data/lib/theme_check/language_server/completion_engine.rb +1 -1
  29. data/lib/theme_check/language_server/completion_providers/object_completion_provider.rb +10 -8
  30. data/lib/theme_check/language_server/constants.rb +5 -1
  31. data/lib/theme_check/language_server/document_link_engine.rb +2 -2
  32. data/lib/theme_check/language_server/handler.rb +32 -24
  33. data/lib/theme_check/language_server/variable_lookup_finder.rb +295 -0
  34. data/lib/theme_check/node.rb +12 -0
  35. data/lib/theme_check/offense.rb +14 -48
  36. data/lib/theme_check/parsing_helpers.rb +1 -1
  37. data/lib/theme_check/position.rb +77 -0
  38. data/lib/theme_check/position_helper.rb +37 -0
  39. data/lib/theme_check/remote_asset_file.rb +3 -0
  40. data/lib/theme_check/shopify_liquid/tag.rb +13 -0
  41. data/lib/theme_check/version.rb +1 -1
  42. data/lib/theme_check/visitor.rb +9 -10
  43. data/theme-check.gemspec +2 -0
  44. metadata +10 -5
  45. data/lib/theme_check/language_server/position_helper.rb +0 -27
@@ -125,5 +125,17 @@ module ThemeCheck
125
125
  start = template.full_line(line_number).index(markup)
126
126
  [start, start + markup.length - 1]
127
127
  end
128
+
129
+ def position
130
+ @position ||= Position.new(markup, template&.source, line_number)
131
+ end
132
+
133
+ def start_index
134
+ position.start_index
135
+ end
136
+
137
+ def end_index
138
+ position.end_index
139
+ end
128
140
  end
129
141
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
- Position = Struct.new(:line, :column)
4
-
5
3
  class Offense
6
4
  MAX_SOURCE_EXCERPT_SIZE = 120
7
5
 
@@ -20,6 +18,7 @@ module ThemeCheck
20
18
  end
21
19
 
22
20
  @node = node
21
+ @template = nil
23
22
  if node
24
23
  @template = node.template
25
24
  elsif template
@@ -40,8 +39,7 @@ module ThemeCheck
40
39
  @node.line_number
41
40
  end
42
41
 
43
- @start_position = nil
44
- @end_position = nil
42
+ @position = Position.new(@markup, @template&.source, @line_number)
45
43
  end
46
44
 
47
45
  def source_excerpt
@@ -56,20 +54,28 @@ module ThemeCheck
56
54
  end
57
55
  end
58
56
 
57
+ def start_index
58
+ @position.start_index
59
+ end
60
+
59
61
  def start_line
60
- start_position.line
62
+ @position.start_row
61
63
  end
62
64
 
63
65
  def start_column
64
- start_position.column
66
+ @position.start_column
67
+ end
68
+
69
+ def end_index
70
+ @position.end_index
65
71
  end
66
72
 
67
73
  def end_line
68
- end_position.line
74
+ @position.end_row
69
75
  end
70
76
 
71
77
  def end_column
72
- end_position.column
78
+ @position.end_column
73
79
  end
74
80
 
75
81
  def code_name
@@ -115,45 +121,5 @@ module ThemeCheck
115
121
  message
116
122
  end
117
123
  end
118
-
119
- private
120
-
121
- def full_line(line)
122
- # Liquid::Template is 1-indexed.
123
- template.full_line(line + 1)
124
- end
125
-
126
- def lines_of_content
127
- @lines ||= markup.lines.map { |x| x.sub(/\n$/, '') }
128
- end
129
-
130
- # 0-indexed, inclusive
131
- def start_position
132
- return @start_position if @start_position
133
- return @start_position = Position.new(0, 0) unless line_number && markup
134
-
135
- position = Position.new
136
- position.line = line_number - 1
137
- position.column = full_line(position.line).index(lines_of_content.first) || 0
138
-
139
- @start_position = position
140
- end
141
-
142
- # 0-indexed, exclusive. It's the line + col that are exclusive.
143
- # This is why it doesn't make sense to calculate them separately.
144
- def end_position
145
- return @end_position if @end_position
146
- return @end_position = Position.new(0, 0) unless line_number && markup
147
-
148
- position = Position.new
149
- position.line = start_line + lines_of_content.size - 1
150
- position.column = if start_line == position.line
151
- start_column + markup.size
152
- else
153
- lines_of_content.last.size
154
- end
155
-
156
- @end_position = position
157
- end
158
124
  end
159
125
  end
@@ -6,7 +6,7 @@ module ThemeCheck
6
6
  scanner = StringScanner.new(markup)
7
7
 
8
8
  while scanner.scan(/.*?("|')/)
9
- yield scanner.matched[..-2]
9
+ yield scanner.matched[0..-2]
10
10
  # Skip to the end of the string
11
11
  scanner.skip_until(scanner.matched[-1] == "'" ? /[^\\]'/ : /[^\\]"/)
12
12
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ class Position
5
+ include PositionHelper
6
+
7
+ def initialize(needle, contents, line_number_1_indexed)
8
+ @needle = needle
9
+ @contents = contents
10
+ @line_number_1_indexed = line_number_1_indexed
11
+ @start_row_column = nil
12
+ @end_row_column = nil
13
+ end
14
+
15
+ def start_line_index
16
+ from_row_column_to_index(contents, line_number, 0)
17
+ end
18
+
19
+ # 0-indexed, inclusive
20
+ def start_index
21
+ contents.index(needle, start_line_index) || start_line_index
22
+ end
23
+
24
+ # 0-indexed, exclusive
25
+ def end_index
26
+ start_index + needle.size
27
+ end
28
+
29
+ def start_row
30
+ start_row_column[0]
31
+ end
32
+
33
+ def start_column
34
+ start_row_column[1]
35
+ end
36
+
37
+ def end_row
38
+ end_row_column[0]
39
+ end
40
+
41
+ def end_column
42
+ end_row_column[1]
43
+ end
44
+
45
+ private
46
+
47
+ def contents
48
+ return '' unless @contents.is_a?(String) && !@contents.empty?
49
+ @contents
50
+ end
51
+
52
+ def line_number
53
+ return 0 if @line_number_1_indexed.nil?
54
+ bounded(0, @line_number_1_indexed - 1, contents.lines.size - 1)
55
+ end
56
+
57
+ def needle
58
+ if @needle.nil? && !contents.empty? && !@line_number_1_indexed.nil?
59
+ contents.lines(chomp: true)[line_number] || ''
60
+ elsif contents.empty? || @needle.nil?
61
+ ''
62
+ else
63
+ @needle
64
+ end
65
+ end
66
+
67
+ def start_row_column
68
+ return @start_row_column unless @start_row_column.nil?
69
+ @start_row_column = from_index_to_row_column(contents, start_index)
70
+ end
71
+
72
+ def end_row_column
73
+ return @end_row_column unless @end_row_column.nil?
74
+ @end_row_column = from_index_to_row_column(contents, end_index)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ # Note: Everything is 0-indexed here.
3
+
4
+ module ThemeCheck
5
+ module PositionHelper
6
+ def from_row_column_to_index(content, row, col)
7
+ return 0 unless content.is_a?(String) && !content.empty?
8
+ return 0 unless row.is_a?(Integer) && col.is_a?(Integer)
9
+ i = 0
10
+ result = 0
11
+ safe_row = bounded(0, row, content.lines.size - 1)
12
+ lines = content.lines
13
+ line_size = lines[i].size
14
+ while i < safe_row
15
+ result += line_size
16
+ i += 1
17
+ line_size = lines[i].size
18
+ end
19
+ result += bounded(0, col, line_size - 1)
20
+ result
21
+ end
22
+
23
+ def from_index_to_row_column(content, index)
24
+ return [0, 0] unless content.is_a?(String) && !content.empty?
25
+ return [0, 0] unless index.is_a?(Integer)
26
+ safe_index = bounded(0, index, content.size - 1)
27
+ lines = content[0..safe_index].lines
28
+ row = lines.size - 1
29
+ col = lines.last.size - 1
30
+ [row, col]
31
+ end
32
+
33
+ def bounded(a, x, b)
34
+ [a, [x, b].min].max
35
+ end
36
+ end
37
+ end
@@ -35,6 +35,9 @@ module ThemeCheck
35
35
  end
36
36
 
37
37
  @content = res.body
38
+
39
+ rescue OpenSSL::SSL::SSLError, Zlib::StreamError, *NET_HTTP_EXCEPTIONS
40
+ @contents = ''
38
41
  end
39
42
 
40
43
  def gzipped_size
@@ -8,6 +8,19 @@ module ThemeCheck
8
8
 
9
9
  def labels
10
10
  @tags ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/tags.yml"))
11
+ .to_set
12
+ end
13
+
14
+ def tag_regex(tag)
15
+ return unless labels.include?(tag)
16
+ @tag_regexes ||= {}
17
+ @tag_regexes[tag] ||= /\A#{Liquid::TagStart}-?\s*#{tag}/m
18
+ end
19
+
20
+ def liquid_tag_regex(tag)
21
+ return unless labels.include?(tag)
22
+ @tag_liquid_regexes ||= {}
23
+ @tag_liquid_regexes[tag] ||= /^\s*#{tag}/m
11
24
  end
12
25
  end
13
26
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
- VERSION = "0.7.2"
3
+ VERSION = "0.8.3"
4
4
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
3
  class Visitor
4
+ attr_reader :checks
5
+
4
6
  def initialize(checks)
5
7
  @checks = checks
6
8
  end
@@ -8,6 +10,7 @@ module ThemeCheck
8
10
  def visit_template(template)
9
11
  @disabled_checks = DisabledChecks.new
10
12
  visit(Node.new(template.root, nil, template))
13
+ remove_disabled_offenses
11
14
  rescue Liquid::Error => exception
12
15
  exception.template_name = template.name
13
16
  call_checks(:on_error, exception)
@@ -29,20 +32,16 @@ module ThemeCheck
29
32
  @disabled_checks.update(node) if node.comment?
30
33
  end
31
34
 
32
- def visit_children(node)
33
- node.children.each { |child| visit(child) }
34
- end
35
-
36
35
  def call_checks(method, *args)
37
36
  checks.call(method, *args)
38
37
  end
39
38
 
40
- def checks
41
- return @checks unless @disabled_checks.any?
42
-
43
- return @checks.always_enabled if @disabled_checks.all_disabled?
44
-
45
- @checks.except_for(@disabled_checks)
39
+ def remove_disabled_offenses
40
+ checks.disableable.each do |check|
41
+ check.offenses.reject! do |offense|
42
+ @disabled_checks.disabled?(offense.code_name, offense.start_index)
43
+ end
44
+ end
46
45
  end
47
46
  end
48
47
  end
data/theme-check.gemspec CHANGED
@@ -13,6 +13,8 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://github.com/Shopify/theme-check"
14
14
  spec.license = "MIT"
15
15
 
16
+ spec.required_ruby_version = ">= 2.6"
17
+
16
18
  spec.metadata['allowed_push_host'] = 'https://rubygems.org'
17
19
 
18
20
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theme-check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Cournoyer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-12 00:00:00.000000000 Z
11
+ date: 2021-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liquid
@@ -104,6 +104,7 @@ files:
104
104
  - lib/theme_check.rb
105
105
  - lib/theme_check/analyzer.rb
106
106
  - lib/theme_check/asset_file.rb
107
+ - lib/theme_check/bug.rb
107
108
  - lib/theme_check/check.rb
108
109
  - lib/theme_check/checks.rb
109
110
  - lib/theme_check/checks/asset_size_css.rb
@@ -138,7 +139,9 @@ files:
138
139
  - lib/theme_check/cli.rb
139
140
  - lib/theme_check/config.rb
140
141
  - lib/theme_check/corrector.rb
142
+ - lib/theme_check/disabled_check.rb
141
143
  - lib/theme_check/disabled_checks.rb
144
+ - lib/theme_check/exceptions.rb
142
145
  - lib/theme_check/file_system_storage.rb
143
146
  - lib/theme_check/in_memory_storage.rb
144
147
  - lib/theme_check/json_check.rb
@@ -155,16 +158,18 @@ files:
155
158
  - lib/theme_check/language_server/constants.rb
156
159
  - lib/theme_check/language_server/document_link_engine.rb
157
160
  - lib/theme_check/language_server/handler.rb
158
- - lib/theme_check/language_server/position_helper.rb
159
161
  - lib/theme_check/language_server/protocol.rb
160
162
  - lib/theme_check/language_server/server.rb
161
163
  - lib/theme_check/language_server/tokens.rb
164
+ - lib/theme_check/language_server/variable_lookup_finder.rb
162
165
  - lib/theme_check/liquid_check.rb
163
166
  - lib/theme_check/locale_diff.rb
164
167
  - lib/theme_check/node.rb
165
168
  - lib/theme_check/offense.rb
166
169
  - lib/theme_check/packager.rb
167
170
  - lib/theme_check/parsing_helpers.rb
171
+ - lib/theme_check/position.rb
172
+ - lib/theme_check/position_helper.rb
168
173
  - lib/theme_check/printer.rb
169
174
  - lib/theme_check/regex_helpers.rb
170
175
  - lib/theme_check/releaser.rb
@@ -196,14 +201,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
196
201
  requirements:
197
202
  - - ">="
198
203
  - !ruby/object:Gem::Version
199
- version: '0'
204
+ version: '2.6'
200
205
  required_rubygems_version: !ruby/object:Gem::Requirement
201
206
  requirements:
202
207
  - - ">="
203
208
  - !ruby/object:Gem::Version
204
209
  version: '0'
205
210
  requirements: []
206
- rubygems_version: 3.0.3
211
+ rubygems_version: 3.2.17
207
212
  signing_key:
208
213
  specification_version: 4
209
214
  summary: A Shopify Theme Linter
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
- # Note: Everything is 0-indexed here.
3
-
4
- module ThemeCheck
5
- module LanguageServer
6
- module PositionHelper
7
- def from_line_column_to_index(content, row, col)
8
- i = 0
9
- result = 0
10
- lines = content.lines
11
- while i < row
12
- result += lines[i].size
13
- i += 1
14
- end
15
- result += col
16
- result
17
- end
18
-
19
- def from_index_to_line_column(content, index)
20
- lines = content[0..index].lines
21
- row = lines.size - 1
22
- col = lines.last.size - 1
23
- [row, col]
24
- end
25
- end
26
- end
27
- end