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
@@ -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)
|