theme-check 1.10.3 → 1.11.0
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/ISSUE_TEMPLATE/bug_report.md +29 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/cla.yml +22 -0
- data/.github/workflows/theme-check.yml +1 -1
- data/CHANGELOG.md +41 -0
- data/README.md +7 -8
- data/config/default.yml +4 -0
- data/data/shopify_liquid/filters.yml +18 -0
- data/dev.yml +1 -1
- data/docs/checks/asset_preload.md +60 -0
- data/docs/checks/asset_size_javascript.md +2 -2
- data/docs/checks/missing_enable_comment.md +3 -3
- data/docs/checks/nested_snippet.md +8 -8
- data/docs/checks/translation_key_exists.md +4 -4
- data/docs/checks/valid_html_translation.md +1 -1
- data/lib/theme_check/analyzer.rb +18 -3
- data/lib/theme_check/check.rb +6 -1
- data/lib/theme_check/checks/asset_preload.rb +20 -0
- data/lib/theme_check/checks/deprecated_filter.rb +29 -5
- data/lib/theme_check/checks/missing_enable_comment.rb +4 -0
- data/lib/theme_check/checks/missing_required_template_files.rb +5 -1
- data/lib/theme_check/checks/missing_template.rb +5 -1
- data/lib/theme_check/checks/unused_assign.rb +6 -1
- data/lib/theme_check/checks/unused_snippet.rb +50 -2
- data/lib/theme_check/config.rb +2 -2
- data/lib/theme_check/disabled_checks.rb +11 -4
- data/lib/theme_check/file_system_storage.rb +2 -0
- data/lib/theme_check/in_memory_storage.rb +1 -1
- data/lib/theme_check/language_server/bridge.rb +31 -6
- data/lib/theme_check/language_server/diagnostics_engine.rb +80 -34
- data/lib/theme_check/language_server/diagnostics_manager.rb +27 -6
- data/lib/theme_check/language_server/execute_command_providers/run_checks_execute_command_provider.rb +7 -6
- data/lib/theme_check/language_server/handler.rb +90 -8
- data/lib/theme_check/language_server/server.rb +42 -14
- data/lib/theme_check/language_server/versioned_in_memory_storage.rb +17 -2
- data/lib/theme_check/liquid_file.rb +22 -1
- data/lib/theme_check/liquid_node.rb +33 -1
- data/lib/theme_check/liquid_visitor.rb +1 -1
- data/lib/theme_check/schema_helper.rb +1 -1
- data/lib/theme_check/tags.rb +2 -1
- data/lib/theme_check/version.rb +1 -1
- data/theme-check.gemspec +2 -2
- metadata +10 -6
- data/.github/probots.yml +0 -3
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'json'
|
3
4
|
require 'stringio'
|
5
|
+
require 'timeout'
|
4
6
|
|
5
7
|
module ThemeCheck
|
6
8
|
module LanguageServer
|
@@ -45,10 +47,29 @@ module ThemeCheck
|
|
45
47
|
def listen
|
46
48
|
start_handler_threads
|
47
49
|
start_json_rpc_thread
|
48
|
-
|
50
|
+
err = @error.pop
|
51
|
+
status_code = status_code_from_error(err)
|
52
|
+
|
53
|
+
if status_code > 0
|
54
|
+
# For a reason I can't comprehend, this hangs but prints
|
55
|
+
# anyway. So it's wrapped in this ugly timeout...
|
56
|
+
Timeout.timeout(1) do
|
57
|
+
$stderr.puts err.full_message
|
58
|
+
end
|
59
|
+
|
60
|
+
# Warn user of error, otherwise server might restart
|
61
|
+
# without telling you.
|
62
|
+
@bridge.send_notification("window/showMessage", {
|
63
|
+
type: 1,
|
64
|
+
message: "A theme-check-language-server error has occurred, search OUTPUT logs for details.",
|
65
|
+
})
|
66
|
+
end
|
67
|
+
|
49
68
|
cleanup(status_code)
|
50
69
|
rescue SignalException
|
51
70
|
0
|
71
|
+
rescue StandardError
|
72
|
+
2
|
52
73
|
end
|
53
74
|
|
54
75
|
def start_json_rpc_thread
|
@@ -65,36 +86,43 @@ module ThemeCheck
|
|
65
86
|
else
|
66
87
|
@queue << message
|
67
88
|
end
|
68
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
69
|
-
break @error << e
|
70
89
|
end
|
90
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
91
|
+
@bridge.log("rescuing #{e.class} in jsonrpc thread")
|
92
|
+
@error << e
|
71
93
|
end
|
72
94
|
end
|
73
95
|
|
74
96
|
def start_handler_threads
|
75
97
|
@number_of_threads.times do
|
76
98
|
@handlers << Thread.new do
|
77
|
-
|
78
|
-
message = @queue.pop
|
79
|
-
break if @queue.closed? && @queue.empty?
|
80
|
-
handle_message(message)
|
81
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
82
|
-
break @error << e
|
83
|
-
end
|
99
|
+
handle_messages
|
84
100
|
end
|
85
101
|
end
|
86
102
|
end
|
87
103
|
|
104
|
+
def handle_messages
|
105
|
+
loop do
|
106
|
+
message = @queue.pop
|
107
|
+
return if @queue.closed? && @queue.empty?
|
108
|
+
|
109
|
+
handle_message(message)
|
110
|
+
end
|
111
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
112
|
+
@bridge.log("rescuing #{e.class} in handler thread")
|
113
|
+
@error << e
|
114
|
+
end
|
115
|
+
|
88
116
|
def status_code_from_error(e)
|
89
117
|
raise e
|
90
118
|
|
91
119
|
# support ctrl+c and stuff
|
92
120
|
rescue SignalException, DoneStreaming
|
93
121
|
0
|
94
|
-
|
95
122
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
96
123
|
raise e if should_raise_errors
|
97
|
-
|
124
|
+
|
125
|
+
@bridge.log("Fatal #{e.class}")
|
98
126
|
2
|
99
127
|
end
|
100
128
|
|
@@ -109,15 +137,14 @@ module ThemeCheck
|
|
109
137
|
if @handler.respond_to?(method_name)
|
110
138
|
@handler.send(method_name, id, params)
|
111
139
|
end
|
112
|
-
|
113
140
|
rescue DoneStreaming => e
|
114
141
|
raise e
|
115
142
|
rescue StandardError => e
|
116
143
|
is_request = id
|
117
144
|
raise e unless is_request
|
145
|
+
|
118
146
|
# Errors obtained in request handlers should be sent
|
119
147
|
# back as internal errors instead of closing the program.
|
120
|
-
@bridge.log("#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}")
|
121
148
|
@bridge.send_internal_error(id, e)
|
122
149
|
end
|
123
150
|
|
@@ -132,6 +159,7 @@ module ThemeCheck
|
|
132
159
|
end
|
133
160
|
|
134
161
|
def cleanup(status_code)
|
162
|
+
@bridge.log("Closing server... status code = #{status_code}")
|
135
163
|
# Stop listenting to RPC calls
|
136
164
|
@messenger.close_input
|
137
165
|
# Wait for rpc loop to close
|
@@ -8,7 +8,7 @@ module ThemeCheck
|
|
8
8
|
|
9
9
|
def initialize(files, root = "/dev/null")
|
10
10
|
super(files, root)
|
11
|
-
@versions = {}
|
11
|
+
@versions = {} # Hash<relative_path, number>
|
12
12
|
@mutex = Mutex.new
|
13
13
|
end
|
14
14
|
|
@@ -49,7 +49,11 @@ module ThemeCheck
|
|
49
49
|
# - Only offer fixes on "clean" files (or offer the change but specify the version so the editor knows what to do with it)
|
50
50
|
def write(relative_path, content, version)
|
51
51
|
@mutex.synchronize do
|
52
|
-
|
52
|
+
if version.nil?
|
53
|
+
@versions.delete(relative_path)
|
54
|
+
else
|
55
|
+
@versions[relative_path] = version
|
56
|
+
end
|
53
57
|
super(relative_path, content)
|
54
58
|
end
|
55
59
|
end
|
@@ -58,6 +62,13 @@ module ThemeCheck
|
|
58
62
|
@mutex.synchronize { [read(relative_path), version(relative_path)] }
|
59
63
|
end
|
60
64
|
|
65
|
+
def remove(relative_path)
|
66
|
+
@mutex.synchronize do
|
67
|
+
@versions.delete(relative_path)
|
68
|
+
super(relative_path)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
61
72
|
def versioned?
|
62
73
|
true
|
63
74
|
end
|
@@ -65,5 +76,9 @@ module ThemeCheck
|
|
65
76
|
def version(relative_path)
|
66
77
|
@versions[relative_path.to_s]
|
67
78
|
end
|
79
|
+
|
80
|
+
def opened_files
|
81
|
+
@versions.keys
|
82
|
+
end
|
68
83
|
end
|
69
84
|
end
|
@@ -33,7 +33,22 @@ module ThemeCheck
|
|
33
33
|
|
34
34
|
def source_excerpt(line)
|
35
35
|
original_lines = source.split("\n")
|
36
|
-
original_lines[line - 1].strip
|
36
|
+
original_lines[bounded(0, line - 1, original_lines.size - 1)].strip
|
37
|
+
rescue => e
|
38
|
+
ThemeCheck.bug(<<~EOS)
|
39
|
+
Exception while running `source_excerpt(#{line})`:
|
40
|
+
```
|
41
|
+
#{e.class}: #{e.message}
|
42
|
+
#{e.backtrace.join("\n ")}
|
43
|
+
```
|
44
|
+
|
45
|
+
path: #{path}
|
46
|
+
|
47
|
+
source:
|
48
|
+
```
|
49
|
+
#{source}
|
50
|
+
```
|
51
|
+
EOS
|
37
52
|
end
|
38
53
|
|
39
54
|
def parse
|
@@ -57,5 +72,11 @@ module ThemeCheck
|
|
57
72
|
disable_liquid_c_nodes: true,
|
58
73
|
)
|
59
74
|
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def bounded(lower, x, upper)
|
79
|
+
[lower, [x, upper].min].max
|
80
|
+
end
|
60
81
|
end
|
61
82
|
end
|
@@ -7,6 +7,7 @@ module ThemeCheck
|
|
7
7
|
|
8
8
|
def initialize(value, parent, theme_file)
|
9
9
|
raise ArgumentError, "Expected a Liquid AST Node" if value.is_a?(LiquidNode)
|
10
|
+
|
10
11
|
@value = value
|
11
12
|
@parent = parent
|
12
13
|
@theme_file = theme_file
|
@@ -37,7 +38,9 @@ module ThemeCheck
|
|
37
38
|
node
|
38
39
|
end
|
39
40
|
end
|
40
|
-
nodes
|
41
|
+
nodes
|
42
|
+
.reject(&:nil?) # We don't want nil nodes, and they can happen
|
43
|
+
.map { |node| LiquidNode.new(node, self, @theme_file) }
|
41
44
|
end
|
42
45
|
end
|
43
46
|
|
@@ -75,11 +78,13 @@ module ThemeCheck
|
|
75
78
|
|
76
79
|
def inner_markup
|
77
80
|
return '' unless block?
|
81
|
+
|
78
82
|
@inner_markup ||= source[block_start_end_index...block_end_start_index]
|
79
83
|
end
|
80
84
|
|
81
85
|
def inner_json
|
82
86
|
return nil unless schema?
|
87
|
+
|
83
88
|
@inner_json ||= JSON.parse(inner_markup)
|
84
89
|
rescue JSON::ParserError
|
85
90
|
# Handled by ValidSchema
|
@@ -153,6 +158,11 @@ module ThemeCheck
|
|
153
158
|
@value.is_a?(Liquid::Comment)
|
154
159
|
end
|
155
160
|
|
161
|
+
# {% # comment %}
|
162
|
+
def inline_comment?
|
163
|
+
@value.is_a?(Liquid::InlineComment)
|
164
|
+
end
|
165
|
+
|
156
166
|
# Top level node of every liquid_file.
|
157
167
|
def document?
|
158
168
|
@value.is_a?(Liquid::Document)
|
@@ -186,6 +196,7 @@ module ThemeCheck
|
|
186
196
|
|
187
197
|
def filters
|
188
198
|
raise TypeError, "Attempting to lookup filters of #{type_name}. Only variables have filters." unless variable?
|
199
|
+
|
189
200
|
@value.filters
|
190
201
|
end
|
191
202
|
|
@@ -193,6 +204,25 @@ module ThemeCheck
|
|
193
204
|
theme_file&.source
|
194
205
|
end
|
195
206
|
|
207
|
+
# For debugging purposes, this might be easier for the eyes.
|
208
|
+
def to_h
|
209
|
+
if literal?
|
210
|
+
return @value
|
211
|
+
elsif variable_lookup?
|
212
|
+
return {
|
213
|
+
type_name: type_name,
|
214
|
+
name: value.name.to_s,
|
215
|
+
lookups: children.map(&:to_h),
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
{
|
220
|
+
type_name: type_name,
|
221
|
+
markup: outer_markup,
|
222
|
+
children: children.map(&:to_h),
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
196
226
|
def block_start_markup
|
197
227
|
source[block_start_start_index...block_start_end_index]
|
198
228
|
end
|
@@ -217,11 +247,13 @@ module ThemeCheck
|
|
217
247
|
|
218
248
|
def block_end_start_index
|
219
249
|
return block_start_end_index unless tag? && block?
|
250
|
+
|
220
251
|
@block_end_start_index ||= block_end_match&.begin(0) || block_start_end_index
|
221
252
|
end
|
222
253
|
|
223
254
|
def block_end_end_index
|
224
255
|
return block_end_start_index unless tag? && block?
|
256
|
+
|
225
257
|
@block_end_end_index ||= block_end_match&.end(0) || block_start_end_index
|
226
258
|
end
|
227
259
|
|
@@ -8,7 +8,7 @@ module ThemeCheck
|
|
8
8
|
path.each_with_index.reduce(hash) do |pointer, (token, index)|
|
9
9
|
if index == path.size - 1
|
10
10
|
pointer[token] = value
|
11
|
-
elsif !pointer.key?(token)
|
11
|
+
elsif !pointer.key?(token) || !pointer[token].is_a?(Hash)
|
12
12
|
pointer[token] = {}
|
13
13
|
end
|
14
14
|
pointer[token]
|
data/lib/theme_check/tags.rb
CHANGED
@@ -145,7 +145,7 @@ module ThemeCheck
|
|
145
145
|
|
146
146
|
disable_tags "include"
|
147
147
|
|
148
|
-
attr_reader :template_name_expr, :attributes
|
148
|
+
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
149
149
|
|
150
150
|
def initialize(tag_name, markup, options)
|
151
151
|
super
|
@@ -171,6 +171,7 @@ module ThemeCheck
|
|
171
171
|
def children
|
172
172
|
[
|
173
173
|
@node.template_name_expr,
|
174
|
+
@node.variable_name_expr,
|
174
175
|
] + @node.attributes.values
|
175
176
|
end
|
176
177
|
end
|
data/lib/theme_check/version.rb
CHANGED
data/theme-check.gemspec
CHANGED
@@ -13,7 +13,7 @@ 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.
|
16
|
+
spec.required_ruby_version = ">= 2.7"
|
17
17
|
|
18
18
|
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
19
19
|
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ["lib"]
|
26
26
|
|
27
|
-
spec.add_dependency('liquid', '>= 5.
|
27
|
+
spec.add_dependency('liquid', '>= 5.4.0')
|
28
28
|
spec.add_dependency('nokogiri', '>= 1.12')
|
29
29
|
spec.add_dependency('parser', '~> 3')
|
30
30
|
end
|
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.11.0
|
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: 2022-
|
11
|
+
date: 2022-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.
|
19
|
+
version: 5.4.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.
|
26
|
+
version: 5.4.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: nokogiri
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -61,7 +61,9 @@ executables:
|
|
61
61
|
extensions: []
|
62
62
|
extra_rdoc_files: []
|
63
63
|
files:
|
64
|
-
- ".github/
|
64
|
+
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
65
|
+
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
66
|
+
- ".github/workflows/cla.yml"
|
65
67
|
- ".github/workflows/theme-check.yml"
|
66
68
|
- ".gitignore"
|
67
69
|
- ".rubocop.yml"
|
@@ -93,6 +95,7 @@ files:
|
|
93
95
|
- docs/api/liquid_check.md
|
94
96
|
- docs/checks/TEMPLATE.md.erb
|
95
97
|
- docs/checks/app_block_valid_tags.md
|
98
|
+
- docs/checks/asset_preload.md
|
96
99
|
- docs/checks/asset_size_app_block_css.md
|
97
100
|
- docs/checks/asset_size_app_block_javascript.md
|
98
101
|
- docs/checks/asset_size_css.md
|
@@ -153,6 +156,7 @@ files:
|
|
153
156
|
- lib/theme_check/checks.rb
|
154
157
|
- lib/theme_check/checks/TEMPLATE.rb.erb
|
155
158
|
- lib/theme_check/checks/app_block_valid_tags.rb
|
159
|
+
- lib/theme_check/checks/asset_preload.rb
|
156
160
|
- lib/theme_check/checks/asset_size_app_block_css.rb
|
157
161
|
- lib/theme_check/checks/asset_size_app_block_javascript.rb
|
158
162
|
- lib/theme_check/checks/asset_size_css.rb
|
@@ -294,7 +298,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
294
298
|
requirements:
|
295
299
|
- - ">="
|
296
300
|
- !ruby/object:Gem::Version
|
297
|
-
version: '2.
|
301
|
+
version: '2.7'
|
298
302
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
299
303
|
requirements:
|
300
304
|
- - ">="
|
data/.github/probots.yml
DELETED