theme-check 0.7.2 → 0.8.3
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 +1 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +33 -0
- data/RELEASING.md +5 -3
- data/config/default.yml +1 -1
- data/data/shopify_liquid/tags.yml +3 -0
- data/data/shopify_translation_keys.yml +1 -0
- data/dev.yml +1 -1
- data/docs/checks/nested_snippet.md +1 -1
- data/docs/checks/space_inside_braces.md +28 -0
- data/exe/theme-check +1 -1
- data/lib/theme_check.rb +5 -0
- data/lib/theme_check/analyzer.rb +19 -9
- data/lib/theme_check/bug.rb +20 -0
- data/lib/theme_check/check.rb +5 -1
- data/lib/theme_check/checks.rb +39 -8
- data/lib/theme_check/checks/missing_enable_comment.rb +4 -4
- data/lib/theme_check/checks/nested_snippet.rb +1 -1
- data/lib/theme_check/checks/space_inside_braces.rb +8 -2
- data/lib/theme_check/cli.rb +99 -64
- data/lib/theme_check/config.rb +6 -2
- data/lib/theme_check/disabled_check.rb +39 -0
- data/lib/theme_check/disabled_checks.rb +20 -32
- data/lib/theme_check/exceptions.rb +32 -0
- data/lib/theme_check/json_file.rb +5 -1
- data/lib/theme_check/language_server.rb +1 -1
- data/lib/theme_check/language_server/completion_engine.rb +1 -1
- data/lib/theme_check/language_server/completion_providers/object_completion_provider.rb +10 -8
- data/lib/theme_check/language_server/constants.rb +5 -1
- data/lib/theme_check/language_server/document_link_engine.rb +2 -2
- data/lib/theme_check/language_server/handler.rb +32 -24
- data/lib/theme_check/language_server/variable_lookup_finder.rb +295 -0
- data/lib/theme_check/node.rb +12 -0
- data/lib/theme_check/offense.rb +14 -48
- data/lib/theme_check/parsing_helpers.rb +1 -1
- data/lib/theme_check/position.rb +77 -0
- data/lib/theme_check/position_helper.rb +37 -0
- data/lib/theme_check/remote_asset_file.rb +3 -0
- data/lib/theme_check/shopify_liquid/tag.rb +13 -0
- data/lib/theme_check/version.rb +1 -1
- data/lib/theme_check/visitor.rb +9 -10
- data/theme-check.gemspec +2 -0
- metadata +10 -5
- data/lib/theme_check/language_server/position_helper.rb +0 -27
data/lib/theme_check/node.rb
CHANGED
@@ -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
|
data/lib/theme_check/offense.rb
CHANGED
@@ -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
|
-
@
|
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
|
-
|
62
|
+
@position.start_row
|
61
63
|
end
|
62
64
|
|
63
65
|
def start_column
|
64
|
-
|
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
|
-
|
74
|
+
@position.end_row
|
69
75
|
end
|
70
76
|
|
71
77
|
def end_column
|
72
|
-
|
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
|
@@ -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
|
data/lib/theme_check/version.rb
CHANGED
data/lib/theme_check/visitor.rb
CHANGED
@@ -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
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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.
|
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-
|
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: '
|
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.
|
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
|