theme-check 0.3.0 → 0.5.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/.rubocop.yml +7 -0
- data/CHANGELOG.md +50 -0
- data/CONTRIBUTING.md +5 -2
- data/README.md +9 -4
- data/RELEASING.md +2 -2
- data/config/default.yml +7 -0
- data/data/shopify_liquid/tags.yml +27 -0
- data/docs/checks/CHECK_DOCS_TEMPLATE.md +47 -0
- data/docs/checks/asset_size_javascript.md +79 -0
- data/docs/checks/convert_include_to_render.md +48 -0
- data/docs/checks/default_locale.md +46 -0
- data/docs/checks/deprecated_filter.md +46 -0
- data/docs/checks/liquid_tag.md +65 -0
- data/docs/checks/matching_schema_translations.md +93 -0
- data/docs/checks/matching_translations.md +72 -0
- data/docs/checks/missing_enable_comment.md +50 -0
- data/docs/checks/missing_required_template_files.md +26 -0
- data/docs/checks/missing_template.md +40 -0
- data/docs/checks/nested_snippet.md +69 -0
- data/docs/checks/parser_blocking_javascript.md +97 -0
- data/docs/checks/required_directories.md +25 -0
- data/docs/checks/required_layout_theme_object.md +28 -0
- data/docs/checks/space_inside_braces.md +63 -0
- data/docs/checks/syntax_error.md +49 -0
- data/docs/checks/template_length.md +50 -0
- data/docs/checks/translation_key_exists.md +63 -0
- data/docs/checks/undefined_object.md +53 -0
- data/docs/checks/unknown_filter.md +45 -0
- data/docs/checks/unused_assign.md +47 -0
- data/docs/checks/unused_snippet.md +32 -0
- data/docs/checks/valid_html_translation.md +53 -0
- data/docs/checks/valid_json.md +60 -0
- data/docs/checks/valid_schema.md +50 -0
- data/lib/theme_check.rb +4 -0
- data/lib/theme_check/asset_file.rb +34 -0
- data/lib/theme_check/check.rb +19 -9
- data/lib/theme_check/checks/asset_size_javascript.rb +74 -0
- data/lib/theme_check/checks/convert_include_to_render.rb +1 -1
- data/lib/theme_check/checks/default_locale.rb +1 -0
- data/lib/theme_check/checks/deprecated_filter.rb +1 -1
- data/lib/theme_check/checks/liquid_tag.rb +3 -3
- data/lib/theme_check/checks/matching_schema_translations.rb +1 -0
- data/lib/theme_check/checks/matching_translations.rb +1 -0
- data/lib/theme_check/checks/missing_enable_comment.rb +1 -0
- data/lib/theme_check/checks/missing_required_template_files.rb +1 -2
- data/lib/theme_check/checks/missing_template.rb +1 -0
- data/lib/theme_check/checks/nested_snippet.rb +1 -0
- data/lib/theme_check/checks/parser_blocking_javascript.rb +2 -1
- data/lib/theme_check/checks/required_directories.rb +1 -1
- data/lib/theme_check/checks/required_layout_theme_object.rb +1 -1
- data/lib/theme_check/checks/space_inside_braces.rb +1 -0
- data/lib/theme_check/checks/syntax_error.rb +1 -0
- data/lib/theme_check/checks/template_length.rb +1 -0
- data/lib/theme_check/checks/translation_key_exists.rb +1 -0
- data/lib/theme_check/checks/undefined_object.rb +29 -10
- data/lib/theme_check/checks/unknown_filter.rb +1 -0
- data/lib/theme_check/checks/unused_assign.rb +5 -3
- data/lib/theme_check/checks/unused_snippet.rb +1 -0
- data/lib/theme_check/checks/valid_html_translation.rb +1 -0
- data/lib/theme_check/checks/valid_json.rb +1 -0
- data/lib/theme_check/checks/valid_schema.rb +1 -0
- data/lib/theme_check/cli.rb +22 -6
- data/lib/theme_check/config.rb +2 -2
- data/lib/theme_check/in_memory_storage.rb +1 -1
- data/lib/theme_check/language_server.rb +10 -0
- data/lib/theme_check/language_server/completion_engine.rb +38 -0
- data/lib/theme_check/language_server/completion_helper.rb +25 -0
- data/lib/theme_check/language_server/completion_provider.rb +24 -0
- data/lib/theme_check/language_server/completion_providers/filter_completion_provider.rb +47 -0
- data/lib/theme_check/language_server/completion_providers/object_completion_provider.rb +31 -0
- data/lib/theme_check/language_server/completion_providers/tag_completion_provider.rb +31 -0
- data/lib/theme_check/language_server/handler.rb +62 -6
- data/lib/theme_check/language_server/position_helper.rb +27 -0
- data/lib/theme_check/language_server/protocol.rb +41 -0
- data/lib/theme_check/language_server/server.rb +6 -1
- data/lib/theme_check/language_server/tokens.rb +55 -0
- data/lib/theme_check/offense.rb +51 -14
- data/lib/theme_check/regex_helpers.rb +15 -0
- data/lib/theme_check/remote_asset_file.rb +44 -0
- data/lib/theme_check/shopify_liquid.rb +1 -0
- data/lib/theme_check/shopify_liquid/tag.rb +16 -0
- data/lib/theme_check/theme.rb +7 -1
- data/lib/theme_check/version.rb +1 -1
- metadata +44 -2
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module ThemeCheck
|
3
|
-
# Recommends using {% liquid ... %} if
|
3
|
+
# Recommends using {% liquid ... %} if 4 or more consecutive {% ... %} are found.
|
4
4
|
class LiquidTag < LiquidCheck
|
5
5
|
severity :suggestion
|
6
6
|
category :liquid
|
7
|
-
doc
|
7
|
+
doc docs_url(__FILE__)
|
8
8
|
|
9
|
-
def initialize(min_consecutive_statements:
|
9
|
+
def initialize(min_consecutive_statements: 4)
|
10
10
|
@first_statement = nil
|
11
11
|
@consecutive_statements = 0
|
12
12
|
@min_consecutive_statements = min_consecutive_statements
|
@@ -3,11 +3,10 @@
|
|
3
3
|
module ThemeCheck
|
4
4
|
# Reports missing shopify required theme files
|
5
5
|
# required templates: https://shopify.dev/tutorials/review-theme-store-requirements-files
|
6
|
-
|
7
6
|
class MissingRequiredTemplateFiles < LiquidCheck
|
8
7
|
severity :error
|
9
8
|
category :liquid
|
10
|
-
doc
|
9
|
+
doc docs_url(__FILE__)
|
11
10
|
|
12
11
|
REQUIRED_LIQUID_FILES = %w(layout/theme)
|
13
12
|
REQUIRED_TEMPLATE_FILES = %w(
|
@@ -3,7 +3,8 @@ module ThemeCheck
|
|
3
3
|
# Reports errors when trying to use parser-blocking script tags
|
4
4
|
class ParserBlockingJavaScript < LiquidCheck
|
5
5
|
severity :error
|
6
|
-
|
6
|
+
categories :liquid, :performance
|
7
|
+
doc docs_url(__FILE__)
|
7
8
|
|
8
9
|
PARSER_BLOCKING_SCRIPT_TAG = %r{
|
9
10
|
<script # Find the start of a script tag
|
@@ -5,7 +5,7 @@ module ThemeCheck
|
|
5
5
|
class RequiredDirectories < LiquidCheck
|
6
6
|
severity :error
|
7
7
|
category :liquid
|
8
|
-
doc
|
8
|
+
doc docs_url(__FILE__)
|
9
9
|
|
10
10
|
REQUIRED_DIRECTORIES = %w(assets config layout locales sections snippets templates)
|
11
11
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
module ThemeCheck
|
3
3
|
class UndefinedObject < LiquidCheck
|
4
4
|
category :liquid
|
5
|
-
doc
|
5
|
+
doc docs_url(__FILE__)
|
6
6
|
severity :error
|
7
7
|
|
8
8
|
class TemplateInfo
|
@@ -21,7 +21,13 @@ module ThemeCheck
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def add_variable_lookup(name:, node:)
|
24
|
-
|
24
|
+
parent = node
|
25
|
+
line_number = nil
|
26
|
+
loop do
|
27
|
+
line_number = parent.line_number
|
28
|
+
parent = parent.parent
|
29
|
+
break unless line_number.nil? && parent
|
30
|
+
end
|
25
31
|
key = [name, line_number]
|
26
32
|
@all_variable_lookups[key] = node
|
27
33
|
end
|
@@ -49,23 +55,28 @@ module ThemeCheck
|
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
52
|
-
def initialize
|
58
|
+
def initialize(exclude_snippets: false)
|
59
|
+
@exclude_snippets = exclude_snippets
|
53
60
|
@files = {}
|
54
61
|
end
|
55
62
|
|
56
63
|
def on_document(node)
|
64
|
+
return if ignore?(node)
|
57
65
|
@files[node.template.name] = TemplateInfo.new
|
58
66
|
end
|
59
67
|
|
60
68
|
def on_assign(node)
|
69
|
+
return if ignore?(node)
|
61
70
|
@files[node.template.name].all_assigns[node.value.to] = node
|
62
71
|
end
|
63
72
|
|
64
73
|
def on_capture(node)
|
74
|
+
return if ignore?(node)
|
65
75
|
@files[node.template.name].all_captures[node.value.instance_variable_get('@to')] = node
|
66
76
|
end
|
67
77
|
|
68
78
|
def on_for(node)
|
79
|
+
return if ignore?(node)
|
69
80
|
@files[node.template.name].all_forloops[node.value.variable_name] = node
|
70
81
|
end
|
71
82
|
|
@@ -75,6 +86,7 @@ module ThemeCheck
|
|
75
86
|
end
|
76
87
|
|
77
88
|
def on_render(node)
|
89
|
+
return if ignore?(node)
|
78
90
|
return unless node.value.template_name_expr.is_a?(String)
|
79
91
|
|
80
92
|
snippet_name = "snippets/#{node.value.template_name_expr}"
|
@@ -85,6 +97,7 @@ module ThemeCheck
|
|
85
97
|
end
|
86
98
|
|
87
99
|
def on_variable_lookup(node)
|
100
|
+
return if ignore?(node)
|
88
101
|
@files[node.template.name].add_variable_lookup(
|
89
102
|
name: node.value.name,
|
90
103
|
node: node,
|
@@ -114,15 +127,20 @@ module ThemeCheck
|
|
114
127
|
end
|
115
128
|
end
|
116
129
|
|
130
|
+
private
|
131
|
+
|
132
|
+
def ignore?(node)
|
133
|
+
@exclude_snippets && node.template.snippet?
|
134
|
+
end
|
135
|
+
|
117
136
|
def each_template
|
118
137
|
@files.each do |(name, info)|
|
119
138
|
next if name.starts_with?('snippets/')
|
120
139
|
yield [name, info]
|
121
140
|
end
|
122
141
|
end
|
123
|
-
private :each_template
|
124
142
|
|
125
|
-
def check_object(info, all_global_objects, render_node = nil)
|
143
|
+
def check_object(info, all_global_objects, render_node = nil, visited_snippets = Set.new)
|
126
144
|
check_undefined(info, all_global_objects, render_node)
|
127
145
|
|
128
146
|
info.each_snippet do |(snippet_name, node)|
|
@@ -131,16 +149,18 @@ module ThemeCheck
|
|
131
149
|
|
132
150
|
snippet_variables = node.value.attributes.keys +
|
133
151
|
Array[node.value.instance_variable_get("@alias_name")]
|
134
|
-
|
152
|
+
unless visited_snippets.include?(snippet_name)
|
153
|
+
visited_snippets << snippet_name
|
154
|
+
check_object(snippet_info, all_global_objects + snippet_variables, node, visited_snippets)
|
155
|
+
end
|
135
156
|
end
|
136
157
|
end
|
137
|
-
private :check_object
|
138
158
|
|
139
159
|
def check_undefined(info, all_global_objects, render_node)
|
140
160
|
all_variables = info.all_variables
|
141
161
|
|
142
162
|
info.each_variable_lookup(!!render_node) do |(key, node)|
|
143
|
-
name,
|
163
|
+
name, line_number = key
|
144
164
|
next if all_variables.include?(name)
|
145
165
|
next if all_global_objects.include?(name)
|
146
166
|
|
@@ -150,10 +170,9 @@ module ThemeCheck
|
|
150
170
|
if render_node
|
151
171
|
add_offense("Missing argument `#{name}`", node: render_node)
|
152
172
|
else
|
153
|
-
add_offense("Undefined object `#{name}`", node: node)
|
173
|
+
add_offense("Undefined object `#{name}`", node: node, line_number: line_number)
|
154
174
|
end
|
155
175
|
end
|
156
176
|
end
|
157
|
-
private :check_undefined
|
158
177
|
end
|
159
178
|
end
|
@@ -4,14 +4,16 @@ module ThemeCheck
|
|
4
4
|
class UnusedAssign < LiquidCheck
|
5
5
|
severity :suggestion
|
6
6
|
category :liquid
|
7
|
+
doc docs_url(__FILE__)
|
7
8
|
|
8
9
|
class TemplateInfo < Struct.new(:used_assigns, :assign_nodes, :includes)
|
9
|
-
def collect_used_assigns(templates)
|
10
|
+
def collect_used_assigns(templates, visited = Set.new)
|
10
11
|
collected = used_assigns
|
11
12
|
# Check recursively inside included snippets for use
|
12
13
|
includes.each do |name|
|
13
|
-
if templates[name]
|
14
|
-
|
14
|
+
if templates[name] && !visited.include?(name)
|
15
|
+
visited << name
|
16
|
+
collected += templates[name].collect_used_assigns(templates, visited)
|
15
17
|
end
|
16
18
|
end
|
17
19
|
collected
|
data/lib/theme_check/cli.rb
CHANGED
@@ -11,6 +11,7 @@ module ThemeCheck
|
|
11
11
|
-x, [--exclude-category] # Exclude this category of checks
|
12
12
|
-l, [--list] # List enabled checks
|
13
13
|
-a, [--auto-correct] # Automatically fix offenses
|
14
|
+
--init # Generate a .theme-check.yml file in the current directory
|
14
15
|
-h, [--help] # Show this. Hi!
|
15
16
|
-v, [--version] # Print Theme Check version
|
16
17
|
|
@@ -22,7 +23,7 @@ module ThemeCheck
|
|
22
23
|
END
|
23
24
|
|
24
25
|
def run(argv)
|
25
|
-
path = "."
|
26
|
+
@path = "."
|
26
27
|
|
27
28
|
command = :check
|
28
29
|
only_categories = []
|
@@ -44,15 +45,19 @@ module ThemeCheck
|
|
44
45
|
command = :list
|
45
46
|
when "--auto-correct", "-a"
|
46
47
|
auto_correct = true
|
48
|
+
when "--init"
|
49
|
+
command = :init
|
47
50
|
else
|
48
|
-
path = arg
|
51
|
+
@path = arg
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
unless [:version, :init].include?(command)
|
56
|
+
@config = ThemeCheck::Config.from_path(@path)
|
57
|
+
@config.only_categories = only_categories
|
58
|
+
@config.exclude_categories = exclude_categories
|
59
|
+
@config.auto_correct = auto_correct
|
60
|
+
end
|
56
61
|
|
57
62
|
send(command)
|
58
63
|
end
|
@@ -75,6 +80,17 @@ module ThemeCheck
|
|
75
80
|
puts ThemeCheck::VERSION
|
76
81
|
end
|
77
82
|
|
83
|
+
def init
|
84
|
+
dotfile_path = ThemeCheck::Config.find(@path)
|
85
|
+
if dotfile_path.nil?
|
86
|
+
File.write(File.join(@path, ThemeCheck::Config::DOTFILE), File.read(ThemeCheck::Config::DEFAULT_CONFIG))
|
87
|
+
|
88
|
+
puts "Writing new #{ThemeCheck::Config::DOTFILE} to #{@path}"
|
89
|
+
else
|
90
|
+
raise Abort, "#{ThemeCheck::Config::DOTFILE} already exists at #{@path}."
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
78
94
|
def check
|
79
95
|
puts "Checking #{@config.root} ..."
|
80
96
|
storage = ThemeCheck::FileSystemStorage.new(@config.root, ignored_patterns: @config.ignored_patterns)
|
data/lib/theme_check/config.rb
CHANGED
@@ -83,8 +83,8 @@ module ThemeCheck
|
|
83
83
|
|
84
84
|
check_class = ThemeCheck.const_get(check_name)
|
85
85
|
|
86
|
-
next if exclude_categories.include?(
|
87
|
-
next if only_categories.any? &&
|
86
|
+
next if check_class.categories.any? { |category| exclude_categories.include?(category) }
|
87
|
+
next if only_categories.any? && check_class.categories.none? { |category| only_categories.include?(category) }
|
88
88
|
|
89
89
|
options_for_check = options.transform_keys(&:to_sym)
|
90
90
|
options_for_check.delete(:enabled)
|
@@ -1,6 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require_relative "language_server/protocol"
|
2
3
|
require_relative "language_server/handler"
|
3
4
|
require_relative "language_server/server"
|
5
|
+
require_relative "language_server/tokens"
|
6
|
+
require_relative "language_server/position_helper"
|
7
|
+
require_relative "language_server/completion_helper"
|
8
|
+
require_relative "language_server/completion_provider"
|
9
|
+
require_relative "language_server/completion_engine"
|
10
|
+
|
11
|
+
Dir[__dir__ + "/language_server/completion_providers/*.rb"].each do |file|
|
12
|
+
require file
|
13
|
+
end
|
4
14
|
|
5
15
|
module ThemeCheck
|
6
16
|
module LanguageServer
|