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
@@ -0,0 +1,47 @@
|
|
1
|
+
# Prevent unused assigns (`UnusedAssign`)
|
2
|
+
|
3
|
+
This check exists to prevent bloat in themes by surfacing variable definitions that are not used.
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This check is aimed at eliminating bloat in themes and highlight user errors.
|
8
|
+
|
9
|
+
:-1: Examples of **incorrect** code for this check:
|
10
|
+
|
11
|
+
```liquid
|
12
|
+
{% assign this_variable_is_not_used = 1 %}
|
13
|
+
```
|
14
|
+
|
15
|
+
:+1: Examples of **correct** code for this check:
|
16
|
+
|
17
|
+
```liquid
|
18
|
+
{% assign this_variable_is_used = 1 %}
|
19
|
+
{% if this_variable_is_used == 1 %}
|
20
|
+
<span>Hello!</span>
|
21
|
+
{% endif %}
|
22
|
+
```
|
23
|
+
|
24
|
+
## Check Options
|
25
|
+
|
26
|
+
The default configuration for this check is the following:
|
27
|
+
|
28
|
+
```yaml
|
29
|
+
UnusedAssign:
|
30
|
+
enabled: true
|
31
|
+
```
|
32
|
+
|
33
|
+
## When Not To Use It
|
34
|
+
|
35
|
+
It's safe to disable this rule.
|
36
|
+
|
37
|
+
## Version
|
38
|
+
|
39
|
+
This check has been introduced in Theme Check 0.1.0.
|
40
|
+
|
41
|
+
## Resources
|
42
|
+
|
43
|
+
- [Rule Source][codesource]
|
44
|
+
- [Documentation Source][docsource]
|
45
|
+
|
46
|
+
[codesource]: /lib/theme_check/checks/unused_assign.rb
|
47
|
+
[docsource]: /docs/checks/unused_assign.md
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Remove unused snippets in themes (`UnusedSnippet`)
|
2
|
+
|
3
|
+
This check warns the user about snippets that are not used (Could not find a `render` tag that uses that snippet)
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This check is aimed at eliminating unused snippets.
|
8
|
+
|
9
|
+
## Check Options
|
10
|
+
|
11
|
+
The default configuration for this check is the following:
|
12
|
+
|
13
|
+
```yaml
|
14
|
+
UnusedSnippet:
|
15
|
+
enabled: true
|
16
|
+
```
|
17
|
+
|
18
|
+
## When Not To Use It
|
19
|
+
|
20
|
+
It's safe to disable this rule.
|
21
|
+
|
22
|
+
## Version
|
23
|
+
|
24
|
+
This check has been introduced in Theme Check 0.1.0.
|
25
|
+
|
26
|
+
## Resources
|
27
|
+
|
28
|
+
- [Rule Source][codesource]
|
29
|
+
- [Documentation Source][docsource]
|
30
|
+
|
31
|
+
[codesource]: /lib/theme_check/checks/unused_snippet.rb
|
32
|
+
[docsource]: /docs/checks/unused_snippet.md
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Prevent invalid HTML inside translations (`ValidHTMLTranslation`)
|
2
|
+
|
3
|
+
This check exists to prevent invalid HTML inside translations.
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This check is aimed at eliminating invalid HTML in translations.
|
8
|
+
|
9
|
+
:-1: Examples of **incorrect** code for this check:
|
10
|
+
|
11
|
+
```liquid
|
12
|
+
{
|
13
|
+
"hello_html": "<h2>Hello, world</h1>",
|
14
|
+
"image_html": "<a href='/spongebob'>Unclosed"
|
15
|
+
}
|
16
|
+
```
|
17
|
+
|
18
|
+
:+1: Examples of **correct** code for this check:
|
19
|
+
|
20
|
+
```liquid
|
21
|
+
{% comment %}locales/en.default.json{% endcomment %}
|
22
|
+
{
|
23
|
+
"hello_html": "<h1>Hello, world</h1>",
|
24
|
+
"image_html": "<img src='spongebob.png'>",
|
25
|
+
"line_break_html": "<br>",
|
26
|
+
"self_closing_svg_html": "<svg />"
|
27
|
+
}
|
28
|
+
```
|
29
|
+
|
30
|
+
## Check Options
|
31
|
+
|
32
|
+
The default configuration for this check is the following:
|
33
|
+
|
34
|
+
```yaml
|
35
|
+
ValidHTMLTranslation:
|
36
|
+
enabled: true
|
37
|
+
```
|
38
|
+
|
39
|
+
## When Not To Use It
|
40
|
+
|
41
|
+
It is discouraged to to disable this rule.
|
42
|
+
|
43
|
+
## Version
|
44
|
+
|
45
|
+
This check has been introduced in Theme Check 0.1.0.
|
46
|
+
|
47
|
+
## Resources
|
48
|
+
|
49
|
+
- [Rule Source][codesource]
|
50
|
+
- [Documentation Source][docsource]
|
51
|
+
|
52
|
+
[codesource]: /lib/theme_check/checks/valid_html_translation.rb
|
53
|
+
[docsource]: /docs/checks/valid_html_translation.md
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Enforce valid JSON (`ValidJson`)
|
2
|
+
|
3
|
+
This check exists to prevent invalid JSON files in themes.
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This check is aimed at eliminating errors in JSON files.
|
8
|
+
|
9
|
+
:-1: Examples of **incorrect** code for this check:
|
10
|
+
|
11
|
+
```json
|
12
|
+
{
|
13
|
+
"comma": "trailing",
|
14
|
+
}
|
15
|
+
```
|
16
|
+
|
17
|
+
```json
|
18
|
+
{
|
19
|
+
"quotes": 'Oops, those are single quotes'
|
20
|
+
}
|
21
|
+
```
|
22
|
+
|
23
|
+
:+1: Examples of **correct** code for this check:
|
24
|
+
|
25
|
+
```json
|
26
|
+
{
|
27
|
+
"comma": "not trailing"
|
28
|
+
}
|
29
|
+
```
|
30
|
+
|
31
|
+
```json
|
32
|
+
{
|
33
|
+
"quotes": "Yes. Double quotes."
|
34
|
+
}
|
35
|
+
```
|
36
|
+
|
37
|
+
## Check Options
|
38
|
+
|
39
|
+
The default configuration for this check is the following:
|
40
|
+
|
41
|
+
```yaml
|
42
|
+
ValidJson:
|
43
|
+
enabled: true
|
44
|
+
```
|
45
|
+
|
46
|
+
## When Not To Use It
|
47
|
+
|
48
|
+
It is not safe to disable this rule.
|
49
|
+
|
50
|
+
## Version
|
51
|
+
|
52
|
+
This check has been introduced in Theme Check 0.1.0.
|
53
|
+
|
54
|
+
## Resources
|
55
|
+
|
56
|
+
- [Rule Source][codesource]
|
57
|
+
- [Documentation Source][docsource]
|
58
|
+
|
59
|
+
[codesource]: /lib/theme_check/checks/valid_json.rb
|
60
|
+
[docsource]: /docs/checks/valid_json.md
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Enforce valid JSON in schema tags (`ValidSchema`)
|
2
|
+
|
3
|
+
This check exists to prevent invalid JSON in `{% schema %}` tags.
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This check is aimed at eliminating JSON errors in schema tags.
|
8
|
+
|
9
|
+
:-1: Examples of **incorrect** code for this check:
|
10
|
+
|
11
|
+
```liquid
|
12
|
+
{% schema %}
|
13
|
+
{
|
14
|
+
"comma": "trailing",
|
15
|
+
}
|
16
|
+
{% endschema %}
|
17
|
+
```
|
18
|
+
|
19
|
+
:+1: Examples of **correct** code for this check:
|
20
|
+
|
21
|
+
```liquid
|
22
|
+
{
|
23
|
+
"comma": "not trailing"
|
24
|
+
}
|
25
|
+
```
|
26
|
+
|
27
|
+
## Check Options
|
28
|
+
|
29
|
+
The default configuration for this check is the following:
|
30
|
+
|
31
|
+
```yaml
|
32
|
+
ValidSchema:
|
33
|
+
enabled: true
|
34
|
+
```
|
35
|
+
|
36
|
+
## When Not To Use It
|
37
|
+
|
38
|
+
It is not safe to disable this check.
|
39
|
+
|
40
|
+
## Version
|
41
|
+
|
42
|
+
This check has been introduced in Theme Check 0.1.0.
|
43
|
+
|
44
|
+
## Resources
|
45
|
+
|
46
|
+
- [Rule Source][codesource]
|
47
|
+
- [Documentation Source][docsource]
|
48
|
+
|
49
|
+
[codesource]: /lib/theme_check/checks/valid_schema.rb
|
50
|
+
[docsource]: /docs/checks/valid_schema.md
|
data/lib/theme_check.rb
CHANGED
@@ -8,6 +8,9 @@ require_relative "theme_check/cli"
|
|
8
8
|
require_relative "theme_check/disabled_checks"
|
9
9
|
require_relative "theme_check/liquid_check"
|
10
10
|
require_relative "theme_check/locale_diff"
|
11
|
+
require_relative "theme_check/asset_file"
|
12
|
+
require_relative "theme_check/remote_asset_file"
|
13
|
+
require_relative "theme_check/regex_helpers"
|
11
14
|
require_relative "theme_check/json_check"
|
12
15
|
require_relative "theme_check/json_file"
|
13
16
|
require_relative "theme_check/json_helpers"
|
@@ -26,5 +29,6 @@ require_relative "theme_check/template"
|
|
26
29
|
require_relative "theme_check/theme"
|
27
30
|
require_relative "theme_check/visitor"
|
28
31
|
require_relative "theme_check/corrector"
|
32
|
+
require_relative "theme_check/version"
|
29
33
|
|
30
34
|
Dir[__dir__ + "/theme_check/checks/*.rb"].each { |file| require file }
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "pathname"
|
3
|
+
require "zlib"
|
4
|
+
|
5
|
+
module ThemeCheck
|
6
|
+
class AssetFile
|
7
|
+
def initialize(relative_path, storage)
|
8
|
+
@relative_path = relative_path
|
9
|
+
@storage = storage
|
10
|
+
@loaded = false
|
11
|
+
@content = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def path
|
15
|
+
@storage.path(@relative_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def relative_path
|
19
|
+
@relative_pathname ||= Pathname.new(@relative_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def content
|
23
|
+
@content ||= @storage.read(@relative_path)
|
24
|
+
end
|
25
|
+
|
26
|
+
def gzipped_size
|
27
|
+
@gzipped_size ||= Zlib.gzip(content).bytesize
|
28
|
+
end
|
29
|
+
|
30
|
+
def name
|
31
|
+
relative_path.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/theme_check/check.rb
CHANGED
@@ -18,7 +18,9 @@ module ThemeCheck
|
|
18
18
|
CATEGORIES = [
|
19
19
|
:liquid,
|
20
20
|
:translation,
|
21
|
+
:performance,
|
21
22
|
:json,
|
23
|
+
:performance,
|
22
24
|
]
|
23
25
|
|
24
26
|
class << self
|
@@ -36,21 +38,29 @@ module ThemeCheck
|
|
36
38
|
@severity if defined?(@severity)
|
37
39
|
end
|
38
40
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
def categories(*categories)
|
42
|
+
@categories ||= []
|
43
|
+
if categories.any?
|
44
|
+
unknown_categories = categories.select { |category| !CATEGORIES.include?(category) }
|
45
|
+
if unknown_categories.any?
|
46
|
+
raise ArgumentError,
|
47
|
+
"unknown categories: #{unknown_categories.join(', ')}. Use: #{CATEGORIES.join(', ')}"
|
43
48
|
end
|
44
|
-
@
|
49
|
+
@categories = categories
|
45
50
|
end
|
46
|
-
@
|
51
|
+
@categories
|
47
52
|
end
|
53
|
+
alias_method :category, :categories
|
48
54
|
|
49
55
|
def doc(doc = nil)
|
50
56
|
@doc = doc if doc
|
51
57
|
@doc if defined?(@doc)
|
52
58
|
end
|
53
59
|
|
60
|
+
def docs_url(path)
|
61
|
+
"https://github.com/Shopify/theme-check/blob/master/docs/checks/#{File.basename(path, '.rb')}.md"
|
62
|
+
end
|
63
|
+
|
54
64
|
def can_disable(disableable = nil)
|
55
65
|
unless disableable.nil?
|
56
66
|
@can_disable = disableable
|
@@ -63,8 +73,8 @@ module ThemeCheck
|
|
63
73
|
self.class.severity
|
64
74
|
end
|
65
75
|
|
66
|
-
def
|
67
|
-
self.class.
|
76
|
+
def categories
|
77
|
+
self.class.categories
|
68
78
|
end
|
69
79
|
|
70
80
|
def doc
|
@@ -93,7 +103,7 @@ module ThemeCheck
|
|
93
103
|
|
94
104
|
def to_s
|
95
105
|
s = +"#{code_name}:\n"
|
96
|
-
properties = { severity: severity,
|
106
|
+
properties = { severity: severity, categories: categories, doc: doc }.merge(options)
|
97
107
|
properties.each_pair do |name, value|
|
98
108
|
s << " #{name}: #{value}\n" if value
|
99
109
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
# Reports errors when trying to use too much JavaScript on page load
|
4
|
+
# Encourages the use of the Import on Interaction pattern [1].
|
5
|
+
# [1]: https://addyosmani.com/blog/import-on-interaction/
|
6
|
+
class AssetSizeJavaScript < LiquidCheck
|
7
|
+
include RegexHelpers
|
8
|
+
severity :error
|
9
|
+
category :performance
|
10
|
+
doc docs_url(__FILE__)
|
11
|
+
|
12
|
+
Script = Struct.new(:src, :match)
|
13
|
+
|
14
|
+
TAG = /#{Liquid::TagStart}.*?#{Liquid::TagEnd}/om
|
15
|
+
VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
|
16
|
+
START_OR_END_QUOTE = /(^['"])|(['"]$)/
|
17
|
+
SCRIPT_TAG_SRC = %r{
|
18
|
+
<script
|
19
|
+
[^>]+ # any non closing tag character
|
20
|
+
src= # src attribute start
|
21
|
+
(?<src>
|
22
|
+
'(?:#{TAG}|#{VARIABLE}|[^']+)*'| # any combination of tag/variable or non straight quote inside straight quotes
|
23
|
+
"(?:#{TAG}|#{VARIABLE}|[^"]+)*" # any combination of tag/variable or non double quotes inside double quotes
|
24
|
+
)
|
25
|
+
[^>]* # any non closing character till the end
|
26
|
+
>
|
27
|
+
}omix
|
28
|
+
|
29
|
+
attr_reader :threshold_in_bytes
|
30
|
+
|
31
|
+
def initialize(threshold_in_bytes: 10000)
|
32
|
+
@threshold_in_bytes = threshold_in_bytes
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_document(node)
|
36
|
+
@node = node
|
37
|
+
@source = node.template.source
|
38
|
+
record_offenses
|
39
|
+
end
|
40
|
+
|
41
|
+
def record_offenses
|
42
|
+
scripts(@source).each do |script|
|
43
|
+
file_size = src_to_file_size(script.src)
|
44
|
+
next if file_size.nil?
|
45
|
+
next if file_size <= threshold_in_bytes
|
46
|
+
add_offense(
|
47
|
+
"JavaScript on every page load exceding compressed size threshold (#{threshold_in_bytes} Bytes), consider using the import on interaction pattern.",
|
48
|
+
node: @node,
|
49
|
+
markup: script.src,
|
50
|
+
line_number: @source[0...script.match.begin(:src)].count("\n") + 1
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def scripts(source)
|
56
|
+
matches(source, SCRIPT_TAG_SRC)
|
57
|
+
.map { |m| Script.new(m[:src].gsub(START_OR_END_QUOTE, ""), m) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def src_to_file_size(src)
|
61
|
+
# We're kind of intentionally only looking at {{ 'asset' | asset_url }} or full urls in here.
|
62
|
+
# More complicated liquid statements are not in scope.
|
63
|
+
if src =~ /^#{VARIABLE}$/o && src =~ /asset_url/ && src =~ Liquid::QuotedString
|
64
|
+
asset_id = Regexp.last_match(0).gsub(START_OR_END_QUOTE, "")
|
65
|
+
asset = @theme.assets.find { |a| a.name.ends_with?("/" + asset_id) }
|
66
|
+
return if asset.nil?
|
67
|
+
asset.gzipped_size
|
68
|
+
elsif src =~ %r{^(https?:)?//}
|
69
|
+
asset = RemoteAssetFile.from_src(src)
|
70
|
+
asset.gzipped_size
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -4,7 +4,7 @@ module ThemeCheck
|
|
4
4
|
class ConvertIncludeToRender < LiquidCheck
|
5
5
|
severity :suggestion
|
6
6
|
category :liquid
|
7
|
-
doc
|
7
|
+
doc docs_url(__FILE__)
|
8
8
|
|
9
9
|
def on_include(node)
|
10
10
|
add_offense("`include` is deprecated - convert it to `render`", node: node)
|