theme-check 1.0.0 → 1.4.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/workflows/theme-check.yml +2 -6
- data/CHANGELOG.md +50 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +39 -0
- data/RELEASING.md +34 -2
- data/bin/theme-check +29 -0
- data/bin/theme-check-language-server +29 -0
- data/config/default.yml +28 -1
- data/config/nothing.yml +11 -0
- data/config/theme_app_extension.yml +168 -0
- data/data/shopify_liquid/objects.yml +1 -0
- data/docs/checks/app_block_valid_tags.md +40 -0
- data/docs/checks/asset_size_app_block_css.md +52 -0
- data/docs/checks/asset_size_app_block_javascript.md +57 -0
- data/docs/checks/deprecate_lazysizes.md +0 -3
- data/docs/checks/missing_template.md +25 -0
- data/docs/checks/pagination_size.md +44 -0
- data/docs/checks/template_length.md +1 -1
- data/docs/checks/undefined_object.md +5 -0
- data/lib/theme_check/analyzer.rb +26 -21
- data/lib/theme_check/asset_file.rb +3 -15
- data/lib/theme_check/bug.rb +3 -1
- data/lib/theme_check/check.rb +26 -4
- data/lib/theme_check/checks/app_block_valid_tags.rb +36 -0
- data/lib/theme_check/checks/asset_size_app_block_css.rb +44 -0
- data/lib/theme_check/checks/asset_size_app_block_javascript.rb +44 -0
- data/lib/theme_check/checks/asset_size_css.rb +3 -3
- data/lib/theme_check/checks/asset_size_javascript.rb +2 -2
- data/lib/theme_check/checks/convert_include_to_render.rb +3 -1
- data/lib/theme_check/checks/default_locale.rb +3 -1
- data/lib/theme_check/checks/deprecate_bgsizes.rb +1 -1
- data/lib/theme_check/checks/deprecate_lazysizes.rb +7 -4
- data/lib/theme_check/checks/img_lazy_loading.rb +1 -1
- data/lib/theme_check/checks/img_width_and_height.rb +3 -3
- data/lib/theme_check/checks/missing_template.rb +21 -5
- data/lib/theme_check/checks/pagination_size.rb +65 -0
- data/lib/theme_check/checks/parser_blocking_javascript.rb +1 -1
- data/lib/theme_check/checks/remote_asset.rb +3 -3
- data/lib/theme_check/checks/space_inside_braces.rb +27 -7
- data/lib/theme_check/checks/template_length.rb +1 -1
- data/lib/theme_check/checks/undefined_object.rb +1 -1
- data/lib/theme_check/checks/valid_html_translation.rb +1 -1
- data/lib/theme_check/checks.rb +11 -1
- data/lib/theme_check/cli.rb +52 -15
- data/lib/theme_check/config.rb +56 -10
- data/lib/theme_check/corrector.rb +9 -0
- data/lib/theme_check/exceptions.rb +29 -27
- data/lib/theme_check/file_system_storage.rb +12 -0
- data/lib/theme_check/html_check.rb +0 -1
- data/lib/theme_check/html_node.rb +37 -16
- data/lib/theme_check/html_visitor.rb +17 -3
- data/lib/theme_check/json_check.rb +2 -2
- data/lib/theme_check/json_file.rb +11 -27
- data/lib/theme_check/json_printer.rb +26 -0
- data/lib/theme_check/language_server/constants.rb +21 -6
- data/lib/theme_check/language_server/document_link_engine.rb +3 -31
- data/lib/theme_check/language_server/document_link_provider.rb +70 -0
- data/lib/theme_check/language_server/document_link_providers/asset_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/include_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/render_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/section_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/handler.rb +7 -4
- data/lib/theme_check/language_server/server.rb +13 -2
- data/lib/theme_check/language_server.rb +5 -0
- data/lib/theme_check/node.rb +6 -4
- data/lib/theme_check/offense.rb +56 -3
- data/lib/theme_check/parsing_helpers.rb +4 -3
- data/lib/theme_check/position.rb +98 -14
- data/lib/theme_check/regex_helpers.rb +5 -2
- data/lib/theme_check/tags.rb +26 -9
- data/lib/theme_check/template.rb +3 -32
- data/lib/theme_check/theme.rb +3 -0
- data/lib/theme_check/theme_file.rb +40 -0
- data/lib/theme_check/version.rb +1 -1
- data/lib/theme_check.rb +16 -0
- data/theme-check.gemspec +1 -1
- metadata +24 -6
- data/bin/liquid-server +0 -4
@@ -0,0 +1,40 @@
|
|
1
|
+
# Reject Forbidden Tags from Theme App Extension Blocks (`AppBlockValidTags`)
|
2
|
+
|
3
|
+
This rule exists to prevent theme app extension blocks from containing forbidden tags in their liquid code.
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This rule verifies none of the below tags are used in theme app extension blocks.
|
8
|
+
|
9
|
+
- `{% javascript %}`
|
10
|
+
- `{% stylesheet %}`
|
11
|
+
- `{% include 'foo' %}`
|
12
|
+
- `{% layout 'foo' %}`
|
13
|
+
- `{% section 'foo' %}`
|
14
|
+
|
15
|
+
:-1: **Incorrect** code for this check occurs with the use of any of the above tags in the liquid code of theme app extension blocks.
|
16
|
+
|
17
|
+
## Check Options
|
18
|
+
|
19
|
+
The default configuration for theme app extensions is the following:
|
20
|
+
|
21
|
+
```yaml
|
22
|
+
AppBlockValidTags:
|
23
|
+
enabled: true
|
24
|
+
```
|
25
|
+
|
26
|
+
## When Not To Use It
|
27
|
+
|
28
|
+
This rule should not be disabled locally.
|
29
|
+
|
30
|
+
## Version
|
31
|
+
|
32
|
+
This check has been introduced in 1.3.0
|
33
|
+
|
34
|
+
## Resources
|
35
|
+
|
36
|
+
- [Rule Source][codesource]
|
37
|
+
- [Documentation Source][docsource]
|
38
|
+
|
39
|
+
[codesource]: /lib/theme_check/checks/app_block_valid_tags.rb
|
40
|
+
[docsource]: /docs/checks/app_block_valid_tags.md
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Prevent Large CSS bundles (`AssetSizeAppBlockCSS`)
|
2
|
+
|
3
|
+
This rule exists to prevent large CSS bundles from being included via Theme App Extensions (for speed).
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This rule disallows the use of too much CSS in themes, as configured by `threshold_in_bytes`.
|
8
|
+
|
9
|
+
:-1: Examples of **incorrect** code for this check:
|
10
|
+
```liquid
|
11
|
+
<!-- Here, assets/app.css is **greater** than `threshold_in_bytes` compressed. -->
|
12
|
+
{% schema %}
|
13
|
+
{
|
14
|
+
...
|
15
|
+
"stylesheet": "app.css"
|
16
|
+
}
|
17
|
+
{% endschema %}
|
18
|
+
```
|
19
|
+
|
20
|
+
## Check Options
|
21
|
+
|
22
|
+
The default configuration is the following:
|
23
|
+
|
24
|
+
```yaml
|
25
|
+
AssetSizeAppBlockCSS:
|
26
|
+
enabled: true
|
27
|
+
threshold_in_bytes: 100_000
|
28
|
+
```
|
29
|
+
|
30
|
+
### `threshold_in_bytes`
|
31
|
+
|
32
|
+
The `threshold_in_bytes` option (default: `100_000`) determines the maximum allowed compressed size in bytes that a single CSS file can take.
|
33
|
+
|
34
|
+
This includes theme and remote stylesheets.
|
35
|
+
|
36
|
+
## When Not To Use It
|
37
|
+
|
38
|
+
This rule should not be disabled locally since the check will be enforced when
|
39
|
+
promoting new versions of the extension.
|
40
|
+
|
41
|
+
## Version
|
42
|
+
|
43
|
+
This check has been introduced in 1.1.0
|
44
|
+
|
45
|
+
## Resources
|
46
|
+
|
47
|
+
- [The Performance Inequality Gap](https://infrequently.org/2021/03/the-performance-inequality-gap/)
|
48
|
+
- [Rule Source][codesource]
|
49
|
+
- [Documentation Source][docsource]
|
50
|
+
|
51
|
+
[codesource]: /lib/theme_check/checks/asset_size_app_block_css.rb
|
52
|
+
[docsource]: /docs/checks/asset_size_app_block_css.md
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Prevent Abuse on Server Rendered App Blocks (`AssetSizeAppBlockJavaScript`)
|
2
|
+
|
3
|
+
For server rendered app blocks, it is an anti-pattern to execute large JavaScript bundles on every page load
|
4
|
+
|
5
|
+
This doesn't mean they don't have a reason to exist. For instance, chat widgets are mini applications embedded inside web pages. Designing such an app with server rendered updates would be absurd. However, if only 10% of the users interact with the chat widget, the other 90% should not have to execute the entire bundle on every page load.
|
6
|
+
|
7
|
+
The natural solution to this problem is to implement the chat widget using the [Import on Interaction Pattern][ioip].
|
8
|
+
|
9
|
+
## Check Details
|
10
|
+
|
11
|
+
This rule disallows the use of block JavaScript files and external scripts to have a compressed size greater than a configured `threshold_in_bytes`.
|
12
|
+
|
13
|
+
:-1: Examples of **incorrect** code for this check:
|
14
|
+
```liquid
|
15
|
+
<!-- Here assets/chat-widget.js is more than 10KB gzipped. -->
|
16
|
+
{% schema %}
|
17
|
+
{
|
18
|
+
...
|
19
|
+
"javascript": "chat-widget.js"
|
20
|
+
}
|
21
|
+
{% endschema %}
|
22
|
+
```
|
23
|
+
|
24
|
+
## Check Options
|
25
|
+
|
26
|
+
The default configuration is the following:
|
27
|
+
|
28
|
+
```yaml
|
29
|
+
AssetSizeAppBlockJavaScript:
|
30
|
+
enabled: true
|
31
|
+
threshold_in_bytes: 10000
|
32
|
+
```
|
33
|
+
|
34
|
+
### `threshold_in_bytes`
|
35
|
+
|
36
|
+
The `threshold_in_bytes` option (default: `10000`) determines the maximum allowed compressed size in bytes that a single JavaScript file can take.
|
37
|
+
|
38
|
+
This includes theme and remote scripts.
|
39
|
+
|
40
|
+
## When Not To Use It
|
41
|
+
|
42
|
+
This rule should not be disabled locally since the check will be enforced when
|
43
|
+
promoting new versions of the extension.
|
44
|
+
|
45
|
+
## Version
|
46
|
+
|
47
|
+
This check has been introduced in 1.1.0
|
48
|
+
|
49
|
+
## Resources
|
50
|
+
|
51
|
+
- [The Import On Interaction Pattern][ioip]
|
52
|
+
- [Rule Source][codesource]
|
53
|
+
- [Documentation Source][docsource]
|
54
|
+
|
55
|
+
[ioip]: https://addyosmani.com/blog/import-on-interaction/
|
56
|
+
[codesource]: /lib/theme_check/checks/asset_size_app_block_javascript.rb
|
57
|
+
[docsource]: /docs/checks/asset_size_app_block_javascript.md
|
@@ -10,9 +10,6 @@ This check is aimed at discouraging the use of the lazysizes JavaScript library
|
|
10
10
|
|
11
11
|
```liquid
|
12
12
|
|
13
|
-
<!-- Reports use of "lazyload" class -->
|
14
|
-
<img src="a.jpg" class="lazyload">
|
15
|
-
|
16
13
|
<!-- Reports use of "data-srcset" and "data-sizes" attribute. Reports data-sizes="auto" -->
|
17
14
|
<img
|
18
15
|
alt="House by the lake"
|
@@ -25,8 +25,33 @@ The default configuration for this check is the following:
|
|
25
25
|
```yaml
|
26
26
|
MissingTemplate:
|
27
27
|
enabled: true
|
28
|
+
ignore_missing: []
|
28
29
|
```
|
29
30
|
|
31
|
+
### `ignore_missing`
|
32
|
+
|
33
|
+
Specify a list of patterns of missing template files to ignore.
|
34
|
+
|
35
|
+
While the `ignore` option will ignore all occurrences of `MissingTemplate` according to the file in which they appear, `ignore_missing` allows ignoring all occurrences of `MissingTemplate` based on the target template, the template being rendered.
|
36
|
+
|
37
|
+
For example:
|
38
|
+
|
39
|
+
```yaml
|
40
|
+
MissingTemplate:
|
41
|
+
ignore_missing:
|
42
|
+
- snippets/icon-*
|
43
|
+
```
|
44
|
+
|
45
|
+
Would ignore offenses on `{% render 'icon-missing' %}` across all theme files.
|
46
|
+
|
47
|
+
```yaml
|
48
|
+
MissingTemplate:
|
49
|
+
ignore:
|
50
|
+
- templates/index.liquid
|
51
|
+
```
|
52
|
+
|
53
|
+
Would ignore all `MissingTemplate` in `templates/index.liquid`, no mater the file being rendered.
|
54
|
+
|
30
55
|
## Version
|
31
56
|
|
32
57
|
This check has been introduced in Theme Check 0.1.0.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Ensure `paginate` tags are used with performant sizes
|
2
|
+
|
3
|
+
## Check Details
|
4
|
+
|
5
|
+
This check is aimed at keeping response times low.
|
6
|
+
|
7
|
+
:-1: Examples of **incorrect** code for this check:
|
8
|
+
|
9
|
+
```liquid
|
10
|
+
<!-- Using too large of page size -->
|
11
|
+
{% paginate collection.products by 999 %}
|
12
|
+
```
|
13
|
+
|
14
|
+
:+1: Examples of **correct** code for this check:
|
15
|
+
|
16
|
+
```liquid
|
17
|
+
{% paginate collection.products by 12 %}
|
18
|
+
```
|
19
|
+
|
20
|
+
Use sizes that are integers below the `max_size`, and above the `min_size`.
|
21
|
+
|
22
|
+
## Check Options
|
23
|
+
|
24
|
+
The default configuration for this check is the following:
|
25
|
+
|
26
|
+
```yaml
|
27
|
+
PaginationSize:
|
28
|
+
enabled: true
|
29
|
+
ignore: []
|
30
|
+
min_size: 1
|
31
|
+
max_size: 50
|
32
|
+
```
|
33
|
+
|
34
|
+
## When Not To Use It
|
35
|
+
|
36
|
+
N/A
|
37
|
+
|
38
|
+
## Version
|
39
|
+
|
40
|
+
This check has been introduced in Theme Check 1.3.0.
|
41
|
+
|
42
|
+
## Resources
|
43
|
+
|
44
|
+
[paginate]: https://shopify.dev/api/liquid/objects/paginate
|
@@ -33,8 +33,13 @@ The default configuration for this check is the following:
|
|
33
33
|
```yaml
|
34
34
|
UndefinedObject:
|
35
35
|
enabled: true
|
36
|
+
exclude_snippets: true
|
36
37
|
```
|
37
38
|
|
39
|
+
### `exclude_snippets`
|
40
|
+
|
41
|
+
The `exclude_snippets` (Default: `true`) option determines whether to check for undefined objects in snippets file (as objects _may_ be defined as arguments)
|
42
|
+
|
38
43
|
## When Not To Use It
|
39
44
|
|
40
45
|
It is discouraged to disable this rule.
|
data/lib/theme_check/analyzer.rb
CHANGED
@@ -34,9 +34,11 @@ module ThemeCheck
|
|
34
34
|
|
35
35
|
liquid_visitor = Visitor.new(@liquid_checks, @disabled_checks)
|
36
36
|
html_visitor = HtmlVisitor.new(@html_checks)
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
ThemeCheck.with_liquid_c_disabled do
|
38
|
+
@theme.liquid.each do |template|
|
39
|
+
liquid_visitor.visit_template(template)
|
40
|
+
html_visitor.visit_template(template)
|
41
|
+
end
|
40
42
|
end
|
41
43
|
|
42
44
|
@theme.json.each { |json_file| @json_checks.call(:on_file, json_file) }
|
@@ -47,24 +49,26 @@ module ThemeCheck
|
|
47
49
|
def analyze_files(files)
|
48
50
|
reset
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
52
|
+
ThemeCheck.with_liquid_c_disabled do
|
53
|
+
# Call all checks that run on the whole theme
|
54
|
+
liquid_visitor = Visitor.new(@liquid_checks.whole_theme, @disabled_checks)
|
55
|
+
html_visitor = HtmlVisitor.new(@html_checks.whole_theme)
|
56
|
+
@theme.liquid.each do |template|
|
57
|
+
liquid_visitor.visit_template(template)
|
58
|
+
html_visitor.visit_template(template)
|
59
|
+
end
|
60
|
+
@theme.json.each { |json_file| @json_checks.whole_theme.call(:on_file, json_file) }
|
61
|
+
|
62
|
+
# Call checks that run on a single files, only on specified file
|
63
|
+
liquid_visitor = Visitor.new(@liquid_checks.single_file, @disabled_checks)
|
64
|
+
html_visitor = HtmlVisitor.new(@html_checks.single_file)
|
65
|
+
files.each do |file|
|
66
|
+
if file.liquid?
|
67
|
+
liquid_visitor.visit_template(file)
|
68
|
+
html_visitor.visit_template(file)
|
69
|
+
elsif file.json?
|
70
|
+
@json_checks.single_file.call(:on_file, file)
|
71
|
+
end
|
68
72
|
end
|
69
73
|
end
|
70
74
|
|
@@ -83,6 +87,7 @@ module ThemeCheck
|
|
83
87
|
if @auto_correct
|
84
88
|
offenses.each(&:correct)
|
85
89
|
@theme.liquid.each(&:write)
|
90
|
+
@theme.json.each(&:write)
|
86
91
|
end
|
87
92
|
end
|
88
93
|
|
@@ -1,27 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "pathname"
|
3
2
|
require "zlib"
|
4
3
|
|
5
4
|
module ThemeCheck
|
6
|
-
class AssetFile
|
5
|
+
class AssetFile < ThemeFile
|
7
6
|
def initialize(relative_path, storage)
|
8
|
-
|
9
|
-
@storage = storage
|
7
|
+
super
|
10
8
|
@loaded = false
|
11
9
|
@content = nil
|
12
10
|
end
|
13
11
|
|
14
|
-
|
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
|
12
|
+
alias_method :content, :source
|
25
13
|
|
26
14
|
def gzipped_size
|
27
15
|
@gzipped_size ||= Zlib.gzip(content).bytesize
|
data/lib/theme_check/bug.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
require 'theme_check/version'
|
3
3
|
|
4
4
|
module ThemeCheck
|
5
|
+
class ThemeCheckError < StandardError; end
|
6
|
+
|
5
7
|
BUG_POSTAMBLE = <<~EOS
|
6
8
|
Theme Check Version: #{VERSION}
|
7
9
|
Ruby Version: #{RUBY_VERSION}
|
@@ -15,6 +17,6 @@ module ThemeCheck
|
|
15
17
|
EOS
|
16
18
|
|
17
19
|
def self.bug(message)
|
18
|
-
|
20
|
+
raise ThemeCheckError, message + BUG_POSTAMBLE
|
19
21
|
end
|
20
22
|
end
|
data/lib/theme_check/check.rb
CHANGED
@@ -9,12 +9,19 @@ module ThemeCheck
|
|
9
9
|
attr_accessor :options, :ignored_patterns
|
10
10
|
attr_writer :offenses
|
11
11
|
|
12
|
+
# The order matters.
|
12
13
|
SEVERITIES = [
|
13
14
|
:error,
|
14
15
|
:suggestion,
|
15
16
|
:style,
|
16
17
|
]
|
17
18
|
|
19
|
+
# [severity: sym] => number
|
20
|
+
SEVERITY_VALUES = SEVERITIES
|
21
|
+
.map
|
22
|
+
.with_index { |sev, i| [sev, i] }
|
23
|
+
.to_h
|
24
|
+
|
18
25
|
CATEGORIES = [
|
19
26
|
:liquid,
|
20
27
|
:translation,
|
@@ -38,6 +45,10 @@ module ThemeCheck
|
|
38
45
|
@severity if defined?(@severity)
|
39
46
|
end
|
40
47
|
|
48
|
+
def severity_value(severity)
|
49
|
+
SEVERITY_VALUES[severity]
|
50
|
+
end
|
51
|
+
|
41
52
|
def categories(*categories)
|
42
53
|
@categories ||= []
|
43
54
|
if categories.any?
|
@@ -58,7 +69,7 @@ module ThemeCheck
|
|
58
69
|
end
|
59
70
|
|
60
71
|
def docs_url(path)
|
61
|
-
"https://github.com/Shopify/theme-check/blob/
|
72
|
+
"https://github.com/Shopify/theme-check/blob/main/docs/checks/#{File.basename(path, '.rb')}.md"
|
62
73
|
end
|
63
74
|
|
64
75
|
def can_disable(disableable = nil)
|
@@ -80,12 +91,23 @@ module ThemeCheck
|
|
80
91
|
@offenses ||= []
|
81
92
|
end
|
82
93
|
|
83
|
-
def add_offense(message, node: nil, template: node&.template, markup: nil, line_number: nil, &block)
|
84
|
-
offenses << Offense.new(check: self, message: message, template: template, node: node, markup: markup, line_number: line_number, correction: block)
|
94
|
+
def add_offense(message, node: nil, template: node&.template, markup: nil, line_number: nil, node_markup_offset: 0, &block)
|
95
|
+
offenses << Offense.new(check: self, message: message, template: template, node: node, markup: markup, line_number: line_number, node_markup_offset: node_markup_offset, correction: block)
|
85
96
|
end
|
86
97
|
|
87
98
|
def severity
|
88
|
-
self.class.severity
|
99
|
+
@severity ||= self.class.severity
|
100
|
+
end
|
101
|
+
|
102
|
+
def severity=(severity)
|
103
|
+
unless SEVERITIES.include?(severity)
|
104
|
+
raise ArgumentError, "unknown severity. Use: #{SEVERITIES.join(', ')}"
|
105
|
+
end
|
106
|
+
@severity = severity
|
107
|
+
end
|
108
|
+
|
109
|
+
def severity_value
|
110
|
+
SEVERITY_VALUES[severity]
|
89
111
|
end
|
90
112
|
|
91
113
|
def categories
|