theme-check 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|