theme-check 1.3.0 → 1.5.2
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/.github/workflows/theme-check.yml +3 -3
- data/.gitignore +1 -0
- data/CHANGELOG.md +38 -0
- data/CONTRIBUTING.md +58 -0
- data/Gemfile +3 -0
- data/config/default.yml +4 -1
- data/data/shopify_liquid/objects.yml +1 -0
- data/docs/checks/deprecate_lazysizes.md +0 -3
- data/docs/checks/deprecated_global_app_block_type.md +65 -0
- data/docs/checks/template_length.md +1 -1
- data/docs/flamegraph.svg +18488 -0
- data/lib/theme_check/analyzer.rb +1 -0
- data/lib/theme_check/checks/default_locale.rb +3 -1
- data/lib/theme_check/checks/deprecate_lazysizes.rb +6 -3
- data/lib/theme_check/checks/deprecated_global_app_block_type.rb +57 -0
- data/lib/theme_check/checks/liquid_tag.rb +1 -1
- data/lib/theme_check/checks/pagination_size.rb +33 -14
- data/lib/theme_check/checks/remote_asset.rb +2 -2
- data/lib/theme_check/checks/required_directories.rb +3 -1
- data/lib/theme_check/checks/space_inside_braces.rb +47 -24
- data/lib/theme_check/checks/template_length.rb +1 -1
- data/lib/theme_check/cli.rb +28 -5
- data/lib/theme_check/corrector.rb +9 -0
- data/lib/theme_check/file_system_storage.rb +6 -0
- data/lib/theme_check/in_memory_storage.rb +4 -0
- data/lib/theme_check/json_file.rb +11 -0
- data/lib/theme_check/json_printer.rb +6 -1
- data/lib/theme_check/language_server/constants.rb +18 -11
- data/lib/theme_check/language_server/document_link_engine.rb +3 -67
- data/lib/theme_check/language_server/document_link_provider.rb +71 -0
- data/lib/theme_check/language_server/document_link_providers/asset_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/include_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/render_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/section_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/handler.rb +17 -13
- data/lib/theme_check/language_server/server.rb +11 -13
- data/lib/theme_check/language_server/uri_helper.rb +37 -0
- data/lib/theme_check/language_server.rb +6 -0
- data/lib/theme_check/node.rb +120 -8
- data/lib/theme_check/position.rb +27 -16
- data/lib/theme_check/position_helper.rb +13 -15
- data/lib/theme_check/printer.rb +9 -5
- data/lib/theme_check/remote_asset_file.rb +4 -0
- data/lib/theme_check/theme.rb +2 -1
- data/lib/theme_check/version.rb +1 -1
- metadata +11 -2
data/lib/theme_check/node.rb
CHANGED
@@ -10,12 +10,14 @@ module ThemeCheck
|
|
10
10
|
@value = value
|
11
11
|
@parent = parent
|
12
12
|
@template = template
|
13
|
+
@tag_markup = nil
|
14
|
+
@line_number_offset = 0
|
13
15
|
end
|
14
16
|
|
15
17
|
# The original source code of the node. Doesn't contain wrapping braces.
|
16
18
|
def markup
|
17
19
|
if tag?
|
18
|
-
|
20
|
+
tag_markup
|
19
21
|
elsif @value.instance_variable_defined?(:@markup)
|
20
22
|
@value.instance_variable_get(:@markup)
|
21
23
|
end
|
@@ -64,6 +66,10 @@ module ThemeCheck
|
|
64
66
|
@value.is_a?(Liquid::Tag)
|
65
67
|
end
|
66
68
|
|
69
|
+
def variable?
|
70
|
+
@value.is_a?(Liquid::Variable)
|
71
|
+
end
|
72
|
+
|
67
73
|
# A {% comment %} block node?
|
68
74
|
def comment?
|
69
75
|
@value.is_a?(Liquid::Comment)
|
@@ -92,7 +98,12 @@ module ThemeCheck
|
|
92
98
|
|
93
99
|
# Most nodes have a line number, but it's not guaranteed.
|
94
100
|
def line_number
|
95
|
-
|
101
|
+
if tag? && @value.respond_to?(:line_number)
|
102
|
+
markup # initialize the line_number_offset
|
103
|
+
@value.line_number - @line_number_offset
|
104
|
+
elsif @value.respond_to?(:line_number)
|
105
|
+
@value.line_number
|
106
|
+
end
|
96
107
|
end
|
97
108
|
|
98
109
|
# The `:under_score_name` of this type of node. Used to dispatch to the `on_<type_name>`
|
@@ -101,19 +112,45 @@ module ThemeCheck
|
|
101
112
|
@type_name ||= StringHelpers.underscore(StringHelpers.demodulize(@value.class.name)).to_sym
|
102
113
|
end
|
103
114
|
|
115
|
+
def source
|
116
|
+
template&.source
|
117
|
+
end
|
118
|
+
|
119
|
+
WHITESPACE = /\s/
|
120
|
+
|
104
121
|
# Is this node inside a `{% liquid ... %}` block?
|
105
122
|
def inside_liquid_tag?
|
106
|
-
|
107
|
-
|
123
|
+
# What we're doing here is starting at the start of the tag and
|
124
|
+
# backtrack on all the whitespace until we land on something. If
|
125
|
+
# that something is {% or %-, then we can safely assume that
|
126
|
+
# we're inside a full tag and not a liquid tag.
|
127
|
+
@inside_liquid_tag ||= if tag? && line_number && source
|
128
|
+
i = 1
|
129
|
+
i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
|
130
|
+
first_two_backtracked_characters = source[(start_index - i - 1)..(start_index - i)]
|
131
|
+
first_two_backtracked_characters != "{%" && first_two_backtracked_characters != "%-"
|
132
|
+
else
|
133
|
+
false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Is this node inside a tag or variable that starts by removing whitespace. i.e. {%- or {{-
|
138
|
+
def whitespace_trimmed_start?
|
139
|
+
@whitespace_trimmed_start ||= if line_number && source && !inside_liquid_tag?
|
140
|
+
i = 1
|
141
|
+
i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
|
142
|
+
source[start_index - i] == "-"
|
108
143
|
else
|
109
144
|
false
|
110
145
|
end
|
111
146
|
end
|
112
147
|
|
113
|
-
# Is this node inside a
|
114
|
-
def
|
115
|
-
if line_number
|
116
|
-
|
148
|
+
# Is this node inside a tag or variable ends starts by removing whitespace. i.e. -%} or -}}
|
149
|
+
def whitespace_trimmed_end?
|
150
|
+
@whitespace_trimmed_end ||= if line_number && source && !inside_liquid_tag?
|
151
|
+
i = 0
|
152
|
+
i += 1 while source[end_index + i] =~ WHITESPACE && i < source.size
|
153
|
+
source[end_index + i] == "-"
|
117
154
|
else
|
118
155
|
false
|
119
156
|
end
|
@@ -132,6 +169,24 @@ module ThemeCheck
|
|
132
169
|
)
|
133
170
|
end
|
134
171
|
|
172
|
+
def start_token
|
173
|
+
return "" if inside_liquid_tag?
|
174
|
+
output = ""
|
175
|
+
output += "{{" if variable?
|
176
|
+
output += "{%" if tag?
|
177
|
+
output += "-" if whitespace_trimmed_start?
|
178
|
+
output
|
179
|
+
end
|
180
|
+
|
181
|
+
def end_token
|
182
|
+
return "" if inside_liquid_tag?
|
183
|
+
output = ""
|
184
|
+
output += "-" if whitespace_trimmed_end?
|
185
|
+
output += "}}" if variable?
|
186
|
+
output += "%}" if tag?
|
187
|
+
output
|
188
|
+
end
|
189
|
+
|
135
190
|
def start_index
|
136
191
|
position.start_index
|
137
192
|
end
|
@@ -139,5 +194,62 @@ module ThemeCheck
|
|
139
194
|
def end_index
|
140
195
|
position.end_index
|
141
196
|
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
# Here we're hacking around a glorious bug in Liquid that makes it so the
|
201
|
+
# line_number and markup of a tag is wrong if there's whitespace
|
202
|
+
# between the tag_name and the markup of the tag.
|
203
|
+
#
|
204
|
+
# {%
|
205
|
+
# render
|
206
|
+
# 'foo'
|
207
|
+
# %}
|
208
|
+
#
|
209
|
+
# Returns a raw value of "render 'foo'\n".
|
210
|
+
# The "\n " between render and 'foo' got replaced by a single space.
|
211
|
+
#
|
212
|
+
# And the line number is the one of 'foo'\n%}. Yay!
|
213
|
+
#
|
214
|
+
# This breaks any kind of position logic we have since that string
|
215
|
+
# does not exist in the template.
|
216
|
+
def tag_markup
|
217
|
+
return @value.raw if @value.instance_variable_get('@markup').empty?
|
218
|
+
return @tag_markup if @tag_markup
|
219
|
+
|
220
|
+
l = 1
|
221
|
+
scanner = StringScanner.new(source)
|
222
|
+
scanner.scan_until(/\n/) while l < @value.line_number && (l += 1)
|
223
|
+
start = scanner.charpos
|
224
|
+
|
225
|
+
tag_markup = @value.instance_variable_get('@markup')
|
226
|
+
|
227
|
+
# See https://github.com/Shopify/theme-check/pull/423/files#r701936559 for a detailed explanation
|
228
|
+
# of why we're doing the check below.
|
229
|
+
#
|
230
|
+
# TL;DR it's because line_numbers are not enough to accurately
|
231
|
+
# determine the position of the raw markup and because that
|
232
|
+
# markup could be present on the same line outside of a Tag. e.g.
|
233
|
+
#
|
234
|
+
# uhoh {% if uhoh %}
|
235
|
+
if (match = /#{@value.tag_name} +#{Regexp.escape(tag_markup)}/.match(source, start))
|
236
|
+
return @tag_markup = match[0]
|
237
|
+
end
|
238
|
+
|
239
|
+
# find the markup
|
240
|
+
markup_start = source.index(tag_markup, start)
|
241
|
+
markup_end = markup_start + tag_markup.size
|
242
|
+
|
243
|
+
# go back until you find the tag_name
|
244
|
+
tag_start = markup_start
|
245
|
+
tag_start -= 1 while source[tag_start - 1] =~ WHITESPACE
|
246
|
+
tag_start -= @value.tag_name.size
|
247
|
+
|
248
|
+
# keep track of the error in line_number
|
249
|
+
@line_number_offset = source[tag_start...markup_start].count("\n")
|
250
|
+
|
251
|
+
# return the real raw content
|
252
|
+
@tag_markup = source[tag_start...markup_end]
|
253
|
+
end
|
142
254
|
end
|
143
255
|
end
|
data/lib/theme_check/position.rb
CHANGED
@@ -16,22 +16,22 @@ module ThemeCheck
|
|
16
16
|
@line_number_1_indexed = line_number_1_indexed
|
17
17
|
@node_markup_offset = node_markup_offset
|
18
18
|
@node_markup = node_markup
|
19
|
-
@strict_position = StrictPosition.new(
|
20
|
-
needle,
|
21
|
-
contents,
|
22
|
-
start_index,
|
23
|
-
)
|
24
19
|
end
|
25
20
|
|
26
21
|
def start_line_offset
|
27
|
-
from_row_column_to_index(contents, line_number, 0)
|
22
|
+
@start_line_offset ||= from_row_column_to_index(contents, line_number, 0)
|
28
23
|
end
|
29
24
|
|
30
25
|
def start_offset
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
26
|
+
@start_offset ||= compute_start_offset
|
27
|
+
end
|
28
|
+
|
29
|
+
def strict_position
|
30
|
+
@strict_position ||= StrictPosition.new(
|
31
|
+
needle,
|
32
|
+
contents,
|
33
|
+
start_index,
|
34
|
+
)
|
35
35
|
end
|
36
36
|
|
37
37
|
# 0-indexed, inclusive
|
@@ -41,39 +41,50 @@ module ThemeCheck
|
|
41
41
|
|
42
42
|
# 0-indexed, exclusive
|
43
43
|
def end_index
|
44
|
-
|
44
|
+
strict_position.end_index
|
45
45
|
end
|
46
46
|
|
47
47
|
# 0-indexed, inclusive
|
48
48
|
def start_row
|
49
|
-
|
49
|
+
strict_position.start_row
|
50
50
|
end
|
51
51
|
|
52
52
|
# 0-indexed, inclusive
|
53
53
|
def start_column
|
54
|
-
|
54
|
+
strict_position.start_column
|
55
55
|
end
|
56
56
|
|
57
57
|
# 0-indexed, exclusive (both taken together are) therefore you
|
58
58
|
# might end up on a newline character or the next line
|
59
59
|
def end_row
|
60
|
-
|
60
|
+
strict_position.end_row
|
61
61
|
end
|
62
62
|
|
63
63
|
def end_column
|
64
|
-
|
64
|
+
strict_position.end_column
|
65
65
|
end
|
66
66
|
|
67
67
|
private
|
68
68
|
|
69
|
+
def compute_start_offset
|
70
|
+
return start_line_offset if @node_markup.nil?
|
71
|
+
node_markup_start = contents.index(@node_markup, start_line_offset)
|
72
|
+
return start_line_offset if node_markup_start.nil?
|
73
|
+
node_markup_start + @node_markup_offset
|
74
|
+
end
|
75
|
+
|
69
76
|
def contents
|
70
77
|
return '' unless @contents.is_a?(String) && !@contents.empty?
|
71
78
|
@contents
|
72
79
|
end
|
73
80
|
|
81
|
+
def content_line_count
|
82
|
+
@content_line_count ||= contents.count("\n")
|
83
|
+
end
|
84
|
+
|
74
85
|
def line_number
|
75
86
|
return 0 if @line_number_1_indexed.nil?
|
76
|
-
bounded(0, @line_number_1_indexed - 1,
|
87
|
+
bounded(0, @line_number_1_indexed - 1, content_line_count)
|
77
88
|
end
|
78
89
|
|
79
90
|
def needle
|
@@ -7,31 +7,29 @@ module ThemeCheck
|
|
7
7
|
return 0 unless content.is_a?(String) && !content.empty?
|
8
8
|
return 0 unless row.is_a?(Integer) && col.is_a?(Integer)
|
9
9
|
i = 0
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
i += 1
|
17
|
-
line_size = lines[i].size
|
18
|
-
end
|
19
|
-
result += bounded(0, col, line_size - 1)
|
20
|
-
result
|
10
|
+
safe_row = bounded(0, row, content.count("\n"))
|
11
|
+
scanner = StringScanner.new(content)
|
12
|
+
scanner.scan_until(/\n/) while i < safe_row && (i += 1)
|
13
|
+
result = scanner.charpos || 0
|
14
|
+
scanner.scan_until(/\n|\z/)
|
15
|
+
bounded(result, result + col, scanner.pre_match.size)
|
21
16
|
end
|
22
17
|
|
23
18
|
def from_index_to_row_column(content, index)
|
24
19
|
return [0, 0] unless content.is_a?(String) && !content.empty?
|
25
20
|
return [0, 0] unless index.is_a?(Integer)
|
26
21
|
safe_index = bounded(0, index, content.size - 1)
|
27
|
-
|
28
|
-
row =
|
29
|
-
col =
|
22
|
+
content_up_to_index = content[0...safe_index]
|
23
|
+
row = content_up_to_index.count("\n")
|
24
|
+
col = 0
|
25
|
+
col += 1 while (safe_index -= 1) && safe_index >= 0 && content[safe_index] != "\n"
|
30
26
|
[row, col]
|
31
27
|
end
|
32
28
|
|
33
29
|
def bounded(a, x, b)
|
34
|
-
|
30
|
+
return a if x < a
|
31
|
+
return b if x > b
|
32
|
+
x
|
35
33
|
end
|
36
34
|
end
|
37
35
|
end
|
data/lib/theme_check/printer.rb
CHANGED
@@ -2,14 +2,18 @@
|
|
2
2
|
|
3
3
|
module ThemeCheck
|
4
4
|
class Printer
|
5
|
+
def initialize(out_stream = STDOUT)
|
6
|
+
@out = out_stream
|
7
|
+
end
|
8
|
+
|
5
9
|
def print(theme, offenses, auto_correct)
|
6
10
|
offenses.each do |offense|
|
7
11
|
print_offense(offense, auto_correct)
|
8
|
-
puts
|
12
|
+
@out.puts
|
9
13
|
end
|
10
14
|
|
11
15
|
correctable = offenses.select(&:correctable?)
|
12
|
-
puts "#{theme.all.size} files inspected, #{red(offenses.size.to_s + ' offenses')} detected, \
|
16
|
+
@out.puts "#{theme.all.size} files inspected, #{red(offenses.size.to_s + ' offenses')} detected, \
|
13
17
|
#{yellow(correctable.size.to_s + ' offenses')} #{auto_correct ? 'corrected' : 'auto-correctable'}"
|
14
18
|
end
|
15
19
|
|
@@ -26,15 +30,15 @@ module ThemeCheck
|
|
26
30
|
""
|
27
31
|
end
|
28
32
|
|
29
|
-
puts location +
|
33
|
+
@out.puts location +
|
30
34
|
colorized_severity(offense.severity) + ": " +
|
31
35
|
yellow(offense.check_name) + ": " +
|
32
36
|
corrected +
|
33
37
|
offense.message + "."
|
34
38
|
if offense.source_excerpt
|
35
|
-
puts "\t#{offense.source_excerpt}"
|
39
|
+
@out.puts "\t#{offense.source_excerpt}"
|
36
40
|
if offense.markup_start_in_excerpt
|
37
|
-
puts "\t" + (" " * offense.markup_start_in_excerpt) + ("^" * offense.markup.size)
|
41
|
+
@out.puts "\t" + (" " * offense.markup_start_in_excerpt) + ("^" * offense.markup.size)
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
@@ -17,6 +17,8 @@ module ThemeCheck
|
|
17
17
|
|
18
18
|
def uri(src)
|
19
19
|
URI.parse(src.sub(%r{^//}, "https://"))
|
20
|
+
rescue URI::InvalidURIError
|
21
|
+
nil
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
@@ -26,6 +28,7 @@ module ThemeCheck
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def content
|
31
|
+
return if @uri.nil?
|
29
32
|
return @content unless @content.nil?
|
30
33
|
|
31
34
|
res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: @uri.scheme == 'https') do |http|
|
@@ -41,6 +44,7 @@ module ThemeCheck
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def gzipped_size
|
47
|
+
return if @uri.nil?
|
44
48
|
@gzipped_size ||= content.bytesize
|
45
49
|
end
|
46
50
|
end
|
data/lib/theme_check/theme.rb
CHANGED
data/lib/theme_check/version.rb
CHANGED
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: 1.
|
4
|
+
version: 1.5.2
|
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-
|
11
|
+
date: 2021-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- docs/checks/deprecate_bgsizes.md
|
91
91
|
- docs/checks/deprecate_lazysizes.md
|
92
92
|
- docs/checks/deprecated_filter.md
|
93
|
+
- docs/checks/deprecated_global_app_block_type.md
|
93
94
|
- docs/checks/html_parsing_error.md
|
94
95
|
- docs/checks/img_lazy_loading.md
|
95
96
|
- docs/checks/img_width_and_height.md
|
@@ -117,6 +118,7 @@ files:
|
|
117
118
|
- docs/checks/valid_html_translation.md
|
118
119
|
- docs/checks/valid_json.md
|
119
120
|
- docs/checks/valid_schema.md
|
121
|
+
- docs/flamegraph.svg
|
120
122
|
- docs/preview.png
|
121
123
|
- exe/theme-check
|
122
124
|
- exe/theme-check-language-server
|
@@ -140,6 +142,7 @@ files:
|
|
140
142
|
- lib/theme_check/checks/deprecate_bgsizes.rb
|
141
143
|
- lib/theme_check/checks/deprecate_lazysizes.rb
|
142
144
|
- lib/theme_check/checks/deprecated_filter.rb
|
145
|
+
- lib/theme_check/checks/deprecated_global_app_block_type.rb
|
143
146
|
- lib/theme_check/checks/html_parsing_error.rb
|
144
147
|
- lib/theme_check/checks/img_lazy_loading.rb
|
145
148
|
- lib/theme_check/checks/img_width_and_height.rb
|
@@ -194,10 +197,16 @@ files:
|
|
194
197
|
- lib/theme_check/language_server/constants.rb
|
195
198
|
- lib/theme_check/language_server/diagnostics_tracker.rb
|
196
199
|
- lib/theme_check/language_server/document_link_engine.rb
|
200
|
+
- lib/theme_check/language_server/document_link_provider.rb
|
201
|
+
- lib/theme_check/language_server/document_link_providers/asset_document_link_provider.rb
|
202
|
+
- lib/theme_check/language_server/document_link_providers/include_document_link_provider.rb
|
203
|
+
- lib/theme_check/language_server/document_link_providers/render_document_link_provider.rb
|
204
|
+
- lib/theme_check/language_server/document_link_providers/section_document_link_provider.rb
|
197
205
|
- lib/theme_check/language_server/handler.rb
|
198
206
|
- lib/theme_check/language_server/protocol.rb
|
199
207
|
- lib/theme_check/language_server/server.rb
|
200
208
|
- lib/theme_check/language_server/tokens.rb
|
209
|
+
- lib/theme_check/language_server/uri_helper.rb
|
201
210
|
- lib/theme_check/language_server/variable_lookup_finder.rb
|
202
211
|
- lib/theme_check/liquid_check.rb
|
203
212
|
- lib/theme_check/locale_diff.rb
|