theme-check 0.10.2 → 1.0.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/CHANGELOG.md +14 -0
- data/config/default.yml +19 -3
- data/data/shopify_liquid/objects.yml +2 -0
- data/docs/checks/asset_size_css_stylesheet_tag.md +50 -0
- data/docs/checks/deprecate_bgsizes.md +66 -0
- data/docs/checks/deprecate_lazysizes.md +61 -0
- data/docs/checks/liquid_tag.md +2 -2
- data/docs/checks/template_length.md +12 -2
- data/lib/theme_check/checks/asset_size_css.rb +11 -74
- data/lib/theme_check/checks/asset_size_css_stylesheet_tag.rb +24 -0
- data/lib/theme_check/checks/asset_size_javascript.rb +10 -36
- data/lib/theme_check/checks/deprecate_bgsizes.rb +14 -0
- data/lib/theme_check/checks/deprecate_lazysizes.rb +16 -0
- data/lib/theme_check/checks/img_lazy_loading.rb +1 -6
- data/lib/theme_check/checks/liquid_tag.rb +2 -2
- data/lib/theme_check/checks/remote_asset.rb +2 -0
- data/lib/theme_check/checks/template_length.rb +18 -4
- data/lib/theme_check/html_check.rb +2 -0
- data/lib/theme_check/liquid_check.rb +0 -12
- data/lib/theme_check/parsing_helpers.rb +3 -1
- data/lib/theme_check/regex_helpers.rb +17 -0
- data/lib/theme_check/tags.rb +37 -0
- data/lib/theme_check/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94b918e53777cfb70c65dacd9d9bfbdb6f4463597c8f9c81969e1baef89df7a1
|
4
|
+
data.tar.gz: 7e45af9a3dfeab1eafdff5533df68a9e0d67007d94661a2c974c0c2fece61095
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea1b75ca11a66aefe5eef421f577d53716cb180799863185d36fad21a9033b5cf52579df3e593e41867513da410adc7d16de723f35b3a5435e50098b7af2a348
|
7
|
+
data.tar.gz: ceab091f6bf7317d899cb5459601b7dfb5294e6e4be962a421b33bad33c74fd730001dea90687f81f754d57e5cdedc5b39d0efb5d95d6c016ed965f7e4e86a9f
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,18 @@
|
|
1
1
|
|
2
|
+
v1.0.0 / 2021-06-28
|
3
|
+
==================
|
4
|
+
|
5
|
+
* Convert `AssetSizeCSS` to `HtmlCheck`
|
6
|
+
* Add `DeprecateLazysizes` & `DeprecateBgsizes` checks
|
7
|
+
* Allow hardcoded CDN urls in `RemoteAsset`
|
8
|
+
* Bump `LiquidTag` `min_consecutive_statements` default to 5
|
9
|
+
* Exclude {% javascript %} and {% stylesheet %} from line counts in `TemplateLength`
|
10
|
+
* Bump `TemplateLength` `max_length` default to 500
|
11
|
+
* Fix `StringScanner#skip(String)` not being supported on some Rubies
|
12
|
+
* Fix `ParsingHelpers#outside_of_strings` handling of empty strings
|
13
|
+
* Update to support new `{% render %}` syntax
|
14
|
+
* Converted `AssetSizeJavaScript` to `HtmlCheck`
|
15
|
+
|
2
16
|
v0.10.2 / 2021-06-18
|
3
17
|
==================
|
4
18
|
|
data/config/default.yml
CHANGED
@@ -12,7 +12,7 @@ ConvertIncludeToRender:
|
|
12
12
|
LiquidTag:
|
13
13
|
enabled: true
|
14
14
|
ignore: []
|
15
|
-
min_consecutive_statements:
|
15
|
+
min_consecutive_statements: 5
|
16
16
|
|
17
17
|
MissingTemplate:
|
18
18
|
enabled: true
|
@@ -38,9 +38,13 @@ SyntaxError:
|
|
38
38
|
TemplateLength:
|
39
39
|
enabled: true
|
40
40
|
ignore: []
|
41
|
-
max_length:
|
41
|
+
max_length: 500
|
42
42
|
# Exclude content of {% schema %} in line count
|
43
43
|
exclude_schema: true
|
44
|
+
# Exclude content of {% stylesheet %} in line count
|
45
|
+
exclude_stylesheet: true
|
46
|
+
# Exclude content of {% javascript %} in line count
|
47
|
+
exclude_javascript: true
|
44
48
|
|
45
49
|
UnknownFilter:
|
46
50
|
enabled: true
|
@@ -99,6 +103,14 @@ DeprecatedFilter:
|
|
99
103
|
enabled: true
|
100
104
|
ignore: []
|
101
105
|
|
106
|
+
DeprecateLazysizes:
|
107
|
+
enabled: true
|
108
|
+
ignore: []
|
109
|
+
|
110
|
+
DeprecateBgsizes:
|
111
|
+
enabled: true
|
112
|
+
ignore: []
|
113
|
+
|
102
114
|
MissingEnableComment:
|
103
115
|
enabled: true
|
104
116
|
ignore: []
|
@@ -109,16 +121,20 @@ ParserBlockingJavaScript:
|
|
109
121
|
|
110
122
|
ParserBlockingScriptTag:
|
111
123
|
enabled: true
|
124
|
+
ignore: []
|
112
125
|
|
113
126
|
AssetSizeJavaScript:
|
114
127
|
enabled: false
|
115
|
-
ignore: []
|
116
128
|
threshold_in_bytes: 10_000
|
117
129
|
ignore: []
|
118
130
|
|
119
131
|
AssetSizeCSS:
|
120
132
|
enabled: false
|
133
|
+
threshold_in_bytes: 100_000
|
121
134
|
ignore: []
|
135
|
+
|
136
|
+
AssetSizeCSSStylesheetTag:
|
137
|
+
enabled: false
|
122
138
|
threshold_in_bytes: 100_000
|
123
139
|
ignore: []
|
124
140
|
|
@@ -40,6 +40,7 @@
|
|
40
40
|
- linklist
|
41
41
|
- linklists
|
42
42
|
- location
|
43
|
+
- localization
|
43
44
|
- metafield
|
44
45
|
- model
|
45
46
|
- model_source
|
@@ -55,6 +56,7 @@
|
|
55
56
|
- powered_by_link
|
56
57
|
- product
|
57
58
|
- product_option
|
59
|
+
- product_variant
|
58
60
|
- recommendations
|
59
61
|
- request
|
60
62
|
- routes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Check Title (`AssetSizeCSSStylesheetTag`)
|
2
|
+
|
3
|
+
The `stylesheet_tag` filter generates a link tag that points to a given stylesheet. This rule exists to prevent large CSS bundles (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/theme.css is **greater** than `threshold_in_bytes` compressed. -->
|
12
|
+
{{ 'theme.css' | asset_url | stylesheet_tag }}
|
13
|
+
```
|
14
|
+
|
15
|
+
:+1: Example of **correct** code for this check:
|
16
|
+
```liquid
|
17
|
+
<!-- Here, assets/theme.css is **less** than `threshold_in_bytes` compressed. -->
|
18
|
+
{{ 'theme.css' | asset_url | stylesheet_tag }}
|
19
|
+
```
|
20
|
+
|
21
|
+
## Check Options
|
22
|
+
|
23
|
+
The default configuration for this check is the following:
|
24
|
+
|
25
|
+
```yaml
|
26
|
+
AssetSizeCSSStylesheetTag:
|
27
|
+
enabled: false
|
28
|
+
threshold_in_bytes: 100_000
|
29
|
+
```
|
30
|
+
|
31
|
+
### `threshold_in_bytes`
|
32
|
+
|
33
|
+
The `threshold_in_bytes` option (default: `100_000`) determines the maximum allowed compressed size in bytes that a single CSS file can take.
|
34
|
+
|
35
|
+
## When Not To Use It
|
36
|
+
|
37
|
+
This rule is safe to disable.
|
38
|
+
|
39
|
+
## Version
|
40
|
+
|
41
|
+
This check has been introduced in Theme Check 1.0.0
|
42
|
+
|
43
|
+
## Resources
|
44
|
+
|
45
|
+
- [The Performance Inequality Gap](https://infrequently.org/2021/03/the-performance-inequality-gap/)
|
46
|
+
- [Rule Source][codesource]
|
47
|
+
- [Documentation Source][docsource]
|
48
|
+
|
49
|
+
[codesource]: /lib/theme_check/checks/asset_size_css_stylesheet_tag.rb
|
50
|
+
[docsource]: /docs/checks/asset_size_css_stylesheet_tag.md
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Deprecate Bgsizes (`DeprecateBgsizes`)
|
2
|
+
|
3
|
+
The lazySizes bgset extension allows you to define multiple background images with a width descriptor. The extension will then load the best image size for the current viewport and device (https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/bgset)
|
4
|
+
|
5
|
+
|
6
|
+
## Check Details
|
7
|
+
|
8
|
+
This check is aimed at discouraging the use of the lazySizes bgset plugin
|
9
|
+
|
10
|
+
:-1: Examples of **incorrect** code for this check:
|
11
|
+
|
12
|
+
```liquid
|
13
|
+
|
14
|
+
<!-- Reports use of "lazyload" class and "data-bgset" attribute -->
|
15
|
+
|
16
|
+
<script src="ls.bgset.min.js"></script>
|
17
|
+
<script src="lazysizes.min.js"></script>
|
18
|
+
<div class="lazyload" data-bgset="image-200.jpg 200w, image-300.jpg 300w, image-400.jpg 400w" data-sizes="auto">
|
19
|
+
</div>
|
20
|
+
|
21
|
+
```
|
22
|
+
|
23
|
+
:+1: Examples of **correct** code for this check:
|
24
|
+
|
25
|
+
```liquid
|
26
|
+
|
27
|
+
<!-- Uses the CSS image-set() attribute instead of "data-bgset" -->
|
28
|
+
<!-- CSS Stylesheet -->
|
29
|
+
.box {
|
30
|
+
background-image: -webkit-image-set(
|
31
|
+
url("small-balloons.jpg") 1x,
|
32
|
+
url("large-balloons.jpg") 2x);
|
33
|
+
background-image: image-set(
|
34
|
+
url("small-balloons.jpg") 1x,
|
35
|
+
url("large-balloons.jpg") 2x);
|
36
|
+
}
|
37
|
+
|
38
|
+
<!-- HTML -->
|
39
|
+
<div class="box"></div>
|
40
|
+
|
41
|
+
```
|
42
|
+
|
43
|
+
## Check Options
|
44
|
+
|
45
|
+
The default configuration for this check is the following:
|
46
|
+
|
47
|
+
```yaml
|
48
|
+
DeprecateBgsizes:
|
49
|
+
enabled: true
|
50
|
+
```
|
51
|
+
|
52
|
+
## When Not To Use It
|
53
|
+
|
54
|
+
You should disable this rule in older browsers that don't support the CSS image-set attribute.
|
55
|
+
|
56
|
+
## Version
|
57
|
+
|
58
|
+
This check has been introduced in Theme Check 1.0.0.
|
59
|
+
|
60
|
+
## Resources
|
61
|
+
|
62
|
+
- [Rule Source][codesource]
|
63
|
+
- [Documentation Source][docsource]
|
64
|
+
|
65
|
+
[codesource]: /lib/theme_check/checks/deprecate_bgsizes.rb
|
66
|
+
[docsource]: /docs/checks/deprecate_bgsizes.md
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Deprecate lazySizes (`DeprecateLazysizes`)
|
2
|
+
|
3
|
+
[lazysizes](https://github.com/aFarkas/lazysizes) is a common JavaScript library used to lazy load images, iframes and scripts.
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This check is aimed at discouraging the use of the lazysizes JavaScript library
|
8
|
+
|
9
|
+
:-1: Examples of **incorrect** code for this check:
|
10
|
+
|
11
|
+
```liquid
|
12
|
+
|
13
|
+
<!-- Reports use of "lazyload" class -->
|
14
|
+
<img src="a.jpg" class="lazyload">
|
15
|
+
|
16
|
+
<!-- Reports use of "data-srcset" and "data-sizes" attribute. Reports data-sizes="auto" -->
|
17
|
+
<img
|
18
|
+
alt="House by the lake"
|
19
|
+
data-sizes="auto"
|
20
|
+
data-srcset="small.jpg 500w,
|
21
|
+
medium.jpg 640w,
|
22
|
+
big.jpg 1024w"
|
23
|
+
data-src="medium.jpg"
|
24
|
+
class="lazyload"
|
25
|
+
/>
|
26
|
+
|
27
|
+
```
|
28
|
+
|
29
|
+
:+1: Examples of **correct** code for this check:
|
30
|
+
|
31
|
+
```liquid
|
32
|
+
|
33
|
+
<!-- Does not use lazySizes library. Instead uses native "loading" attribute -->
|
34
|
+
<img src="a.jpg" loading="lazy">
|
35
|
+
|
36
|
+
```
|
37
|
+
|
38
|
+
## Check Options
|
39
|
+
|
40
|
+
The default configuration for this check is the following:
|
41
|
+
|
42
|
+
```yaml
|
43
|
+
DeprecateLazysizes:
|
44
|
+
enabled: true
|
45
|
+
```
|
46
|
+
|
47
|
+
## When Not To Use It
|
48
|
+
|
49
|
+
You should disable this rule if you want to support lazy loading of images in older browser that don't support the loading="lazy" attribute yet.
|
50
|
+
|
51
|
+
## Version
|
52
|
+
|
53
|
+
This check has been introduced in Theme Check 1.0.0.
|
54
|
+
|
55
|
+
## Resources
|
56
|
+
|
57
|
+
- [Rule Source][codesource]
|
58
|
+
- [Documentation Source][docsource]
|
59
|
+
|
60
|
+
[codesource]: /lib/theme_check/checks/deprecate_lazysizes.rb
|
61
|
+
[docsource]: /docs/checks/deprecate_lazysizes.md
|
data/docs/checks/liquid_tag.md
CHANGED
@@ -39,12 +39,12 @@ The default configuration for this check is the following:
|
|
39
39
|
```yaml
|
40
40
|
LiquidTag:
|
41
41
|
enabled: true
|
42
|
-
min_consecutive_statements:
|
42
|
+
min_consecutive_statements: 5
|
43
43
|
```
|
44
44
|
|
45
45
|
### `min_consecutive_statements`
|
46
46
|
|
47
|
-
The `min_consecutive_statements` option (Default: `
|
47
|
+
The `min_consecutive_statements` option (Default: `5`) determines the maximum (inclusive) number of consecutive statements before the check recommends a refactor.
|
48
48
|
|
49
49
|
## When Not To Use It
|
50
50
|
|
@@ -21,8 +21,10 @@ The default configuration for this check is the following:
|
|
21
21
|
```yaml
|
22
22
|
TemplateLength:
|
23
23
|
enabled: true
|
24
|
-
max_length:
|
24
|
+
max_length: 500
|
25
25
|
exclude_schema: true
|
26
|
+
exclude_stylesheet: true
|
27
|
+
exclude_javascript: true
|
26
28
|
```
|
27
29
|
|
28
30
|
### `max_length`
|
@@ -31,7 +33,15 @@ The `max_length` (Default: `200`) option determines the maximum number of lines
|
|
31
33
|
|
32
34
|
### `exclude_schema`
|
33
35
|
|
34
|
-
The `exclude_schema` (Default: `true`) option determines if the schema
|
36
|
+
The `exclude_schema` (Default: `true`) option determines if the lines inside `{% schema %}` blocks from a template should be excluded from the line count.
|
37
|
+
|
38
|
+
### `exclude_stylesheet`
|
39
|
+
|
40
|
+
The `exclude_stylesheet` (Default: `true`) option determines if the lines inside `{% stylesheet %}` blocks from a template should be excluded from the line count.
|
41
|
+
|
42
|
+
### `exclude_javascript`
|
43
|
+
|
44
|
+
The `exclude_javascript` (Default: `true`) option determines if the lines inside `{% javascript %}` blocks from a template should be excluded from the line count.
|
35
45
|
|
36
46
|
## When Not To Use It
|
37
47
|
|
@@ -1,89 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module ThemeCheck
|
3
|
-
class AssetSizeCSS <
|
3
|
+
class AssetSizeCSS < HtmlCheck
|
4
4
|
include RegexHelpers
|
5
5
|
severity :error
|
6
|
-
category :performance
|
6
|
+
category :html, :performance
|
7
7
|
doc docs_url(__FILE__)
|
8
8
|
|
9
|
-
Link = Struct.new(:href, :index)
|
10
|
-
|
11
|
-
LINK_TAG_HREF = %r{
|
12
|
-
<link
|
13
|
-
(?=[^>]+?rel=['"]?stylesheet['"]?) # Make sure rel=stylesheet is in the link with lookahead
|
14
|
-
[^>]+ # any non closing tag character
|
15
|
-
href= # href attribute start
|
16
|
-
(?<href>#{QUOTED_LIQUID_ATTRIBUTE}) # href attribute value (may contain liquid)
|
17
|
-
[^>]* # any non closing character till the end
|
18
|
-
>
|
19
|
-
}omix
|
20
|
-
STYLESHEET_TAG = %r{
|
21
|
-
#{Liquid::VariableStart} # VariableStart
|
22
|
-
(?:(?!#{Liquid::VariableEnd}).)*? # anything that isn't followed by a VariableEnd
|
23
|
-
\|\s*asset_url\s* # | asset_url
|
24
|
-
\|\s*stylesheet_tag\s* # | stylesheet_tag
|
25
|
-
#{Liquid::VariableEnd} # VariableEnd
|
26
|
-
}omix
|
27
|
-
|
28
9
|
attr_reader :threshold_in_bytes
|
29
10
|
|
30
11
|
def initialize(threshold_in_bytes: 100_000)
|
31
12
|
@threshold_in_bytes = threshold_in_bytes
|
32
13
|
end
|
33
14
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
next if file_size.nil?
|
44
|
-
next if file_size <= threshold_in_bytes
|
45
|
-
add_offense(
|
46
|
-
"CSS on every page load exceding compressed size threshold (#{threshold_in_bytes} Bytes).",
|
47
|
-
node: @node,
|
48
|
-
markup: stylesheet.href,
|
49
|
-
line_number: @source[0...stylesheet.index].count("\n") + 1
|
50
|
-
)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def stylesheets(source)
|
55
|
-
stylesheet_links = matches(source, LINK_TAG_HREF)
|
56
|
-
.map do |m|
|
57
|
-
Link.new(
|
58
|
-
m[:href].gsub(START_OR_END_QUOTE, ""),
|
59
|
-
m.begin(:href),
|
60
|
-
)
|
61
|
-
end
|
62
|
-
|
63
|
-
stylesheet_tags = matches(source, STYLESHEET_TAG)
|
64
|
-
.map do |m|
|
65
|
-
Link.new(
|
66
|
-
m[0],
|
67
|
-
m.begin(0),
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
|
-
stylesheet_links + stylesheet_tags
|
72
|
-
end
|
73
|
-
|
74
|
-
def href_to_file_size(href)
|
75
|
-
# asset_url (+ optional stylesheet_tag) variables
|
76
|
-
if href =~ /^#{VARIABLE}$/o && href =~ /asset_url/ && href =~ Liquid::QuotedString
|
77
|
-
asset_id = Regexp.last_match(0).gsub(START_OR_END_QUOTE, "")
|
78
|
-
asset = @theme.assets.find { |a| a.name.end_with?("/" + asset_id) }
|
79
|
-
return if asset.nil?
|
80
|
-
asset.gzipped_size
|
81
|
-
|
82
|
-
# remote URLs
|
83
|
-
elsif href =~ %r{^(https?:)?//}
|
84
|
-
asset = RemoteAssetFile.from_src(href)
|
85
|
-
asset.gzipped_size
|
86
|
-
end
|
15
|
+
def on_link(node)
|
16
|
+
return if node.attributes['rel']&.value != "stylesheet"
|
17
|
+
file_size = href_to_file_size(node.attributes['href']&.value)
|
18
|
+
return if file_size.nil?
|
19
|
+
return if file_size <= threshold_in_bytes
|
20
|
+
add_offense(
|
21
|
+
"CSS on every page load exceeding compressed size threshold (#{threshold_in_bytes} Bytes).",
|
22
|
+
node: node
|
23
|
+
)
|
87
24
|
end
|
88
25
|
end
|
89
26
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
class AssetSizeCSSStylesheetTag < LiquidCheck
|
4
|
+
include RegexHelpers
|
5
|
+
severity :error
|
6
|
+
category :liquid, :performance
|
7
|
+
doc docs_url(__FILE__)
|
8
|
+
|
9
|
+
def initialize(threshold_in_bytes: 100_000)
|
10
|
+
@threshold_in_bytes = threshold_in_bytes
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_variable(node)
|
14
|
+
used_filters = node.value.filters.map { |name, *_rest| name }
|
15
|
+
return unless used_filters.include?("stylesheet_tag")
|
16
|
+
file_size = href_to_file_size('{{' + node.markup + '}}')
|
17
|
+
return if file_size <= @threshold_in_bytes
|
18
|
+
add_offense(
|
19
|
+
"CSS on every page load exceeding compressed size threshold (#{@threshold_in_bytes} Bytes).",
|
20
|
+
node: node
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -3,52 +3,26 @@ module ThemeCheck
|
|
3
3
|
# Reports errors when trying to use too much JavaScript on page load
|
4
4
|
# Encourages the use of the Import on Interaction pattern [1].
|
5
5
|
# [1]: https://addyosmani.com/blog/import-on-interaction/
|
6
|
-
class AssetSizeJavaScript <
|
6
|
+
class AssetSizeJavaScript < HtmlCheck
|
7
7
|
include RegexHelpers
|
8
8
|
severity :error
|
9
|
-
category :performance
|
9
|
+
category :html, :performance
|
10
10
|
doc docs_url(__FILE__)
|
11
11
|
|
12
|
-
Script = Struct.new(:src, :match)
|
13
|
-
|
14
|
-
SCRIPT_TAG_SRC = %r{
|
15
|
-
<script
|
16
|
-
[^>]+ # any non closing tag character
|
17
|
-
src= # src attribute start
|
18
|
-
(?<src>#{QUOTED_LIQUID_ATTRIBUTE}) # src attribute value (may contain liquid)
|
19
|
-
[^>]* # any non closing character till the end
|
20
|
-
>
|
21
|
-
}omix
|
22
|
-
|
23
12
|
attr_reader :threshold_in_bytes
|
24
13
|
|
25
14
|
def initialize(threshold_in_bytes: 10000)
|
26
15
|
@threshold_in_bytes = threshold_in_bytes
|
27
16
|
end
|
28
17
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
file_size = src_to_file_size(script.src)
|
38
|
-
next if file_size.nil?
|
39
|
-
next if file_size <= threshold_in_bytes
|
40
|
-
add_offense(
|
41
|
-
"JavaScript on every page load exceding compressed size threshold (#{threshold_in_bytes} Bytes), consider using the import on interaction pattern.",
|
42
|
-
node: @node,
|
43
|
-
markup: script.src,
|
44
|
-
line_number: @source[0...script.match.begin(:src)].count("\n") + 1
|
45
|
-
)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def scripts(source)
|
50
|
-
matches(source, SCRIPT_TAG_SRC)
|
51
|
-
.map { |m| Script.new(m[:src].gsub(START_OR_END_QUOTE, ""), m) }
|
18
|
+
def on_script(node)
|
19
|
+
file_size = src_to_file_size(node.attributes['src']&.value)
|
20
|
+
return if file_size.nil?
|
21
|
+
return if file_size <= threshold_in_bytes
|
22
|
+
add_offense(
|
23
|
+
"JavaScript on every page load exceeds compressed size threshold (#{threshold_in_bytes} Bytes), consider using the import on interaction pattern.",
|
24
|
+
node: node
|
25
|
+
)
|
52
26
|
end
|
53
27
|
|
54
28
|
def src_to_file_size(src)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
class DeprecateBgsizes < HtmlCheck
|
4
|
+
severity :suggestion
|
5
|
+
category :html, :performance
|
6
|
+
doc docs_url(__FILE__)
|
7
|
+
|
8
|
+
def on_div(node)
|
9
|
+
class_list = node.attributes["class"]&.value&.split(" ")
|
10
|
+
add_offense("Use the native loading=\"lazy\" attribute instead of lazysizes", node: node) if class_list&.include?("lazyload")
|
11
|
+
add_offense("Use the CSS imageset attribute instead of data-bgset", node: node) if node.attributes["data-bgset"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
class DeprecateLazysizes < HtmlCheck
|
4
|
+
severity :suggestion
|
5
|
+
category :html, :performance
|
6
|
+
doc docs_url(__FILE__)
|
7
|
+
|
8
|
+
def on_img(node)
|
9
|
+
class_list = node.attributes["class"]&.value&.split(" ")
|
10
|
+
add_offense("Use the native loading=\"lazy\" attribute instead of lazysizes", node: node) if class_list&.include?("lazyload")
|
11
|
+
add_offense("Use the native srcset attribute instead of data-srcset", node: node) if node.attributes["data-srcset"]
|
12
|
+
add_offense("Use the native sizes attribute instead of data-sizes", node: node) if node.attributes["data-sizes"]
|
13
|
+
add_offense("Do not set the data-sizes attribute to auto", node: node) if node.attributes["data-sizes"]&.value == "auto"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -10,12 +10,7 @@ module ThemeCheck
|
|
10
10
|
def on_img(node)
|
11
11
|
loading = node.attributes["loading"]&.value&.downcase
|
12
12
|
return if ACCEPTED_LOADING_VALUES.include?(loading)
|
13
|
-
|
14
|
-
class_list = node.attributes["class"]&.value&.split(" ")
|
15
|
-
|
16
|
-
if class_list&.include?("lazyload")
|
17
|
-
add_offense("Use the native loading=\"lazy\" attribute instead of lazysizes", node: node)
|
18
|
-
elsif loading == "auto"
|
13
|
+
if loading == "auto"
|
19
14
|
add_offense("Prefer loading=\"lazy\" to defer loading of images", node: node)
|
20
15
|
else
|
21
16
|
add_offense("Add a loading=\"lazy\" attribute to defer loading of images", node: node)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module ThemeCheck
|
3
|
-
# Recommends using {% liquid ... %} if
|
3
|
+
# Recommends using {% liquid ... %} if 5 or more consecutive {% ... %} are found.
|
4
4
|
class LiquidTag < LiquidCheck
|
5
5
|
severity :suggestion
|
6
6
|
category :liquid
|
7
7
|
doc docs_url(__FILE__)
|
8
8
|
|
9
|
-
def initialize(min_consecutive_statements:
|
9
|
+
def initialize(min_consecutive_statements: 5)
|
10
10
|
@first_statement = nil
|
11
11
|
@consecutive_statements = 0
|
12
12
|
@min_consecutive_statements = min_consecutive_statements
|
@@ -9,6 +9,7 @@ module ThemeCheck
|
|
9
9
|
PROTOCOL = %r{(https?:)?//}
|
10
10
|
ABSOLUTE_PATH = %r{\A/[^/]}im
|
11
11
|
RELATIVE_PATH = %r{\A(?!#{PROTOCOL})[^/\{]}oim
|
12
|
+
CDN_ROOT = "https://cdn.shopify.com/"
|
12
13
|
|
13
14
|
def on_element(node)
|
14
15
|
return unless TAGS.include?(node.name)
|
@@ -17,6 +18,7 @@ module ThemeCheck
|
|
17
18
|
return if resource_url.nil? || resource_url.empty?
|
18
19
|
|
19
20
|
# Ignore if URL is Liquid, taken care of by AssetUrlFilters check
|
21
|
+
return if resource_url.start_with?(CDN_ROOT)
|
20
22
|
return if resource_url =~ ABSOLUTE_PATH
|
21
23
|
return if resource_url =~ RELATIVE_PATH
|
22
24
|
return if url_hosted_by_shopify?(resource_url)
|
@@ -5,9 +5,11 @@ module ThemeCheck
|
|
5
5
|
category :liquid
|
6
6
|
doc docs_url(__FILE__)
|
7
7
|
|
8
|
-
def initialize(max_length:
|
8
|
+
def initialize(max_length: 500, exclude_schema: true, exclude_stylesheet: true, exclude_javascript: true)
|
9
9
|
@max_length = max_length
|
10
10
|
@exclude_schema = exclude_schema
|
11
|
+
@exclude_stylesheet = exclude_stylesheet
|
12
|
+
@exclude_javascript = exclude_javascript
|
11
13
|
end
|
12
14
|
|
13
15
|
def on_document(_node)
|
@@ -15,9 +17,15 @@ module ThemeCheck
|
|
15
17
|
end
|
16
18
|
|
17
19
|
def on_schema(node)
|
18
|
-
if @exclude_schema
|
19
|
-
|
20
|
-
|
20
|
+
exclude_node_lines(node) if @exclude_schema
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_stylesheet(node)
|
24
|
+
exclude_node_lines(node) if @exclude_stylesheet
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_javascript(node)
|
28
|
+
exclude_node_lines(node) if @exclude_javascript
|
21
29
|
end
|
22
30
|
|
23
31
|
def after_document(node)
|
@@ -26,5 +34,11 @@ module ThemeCheck
|
|
26
34
|
add_offense("Template has too many lines [#{lines}/#{@max_length}]", template: node.template)
|
27
35
|
end
|
28
36
|
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def exclude_node_lines(node)
|
41
|
+
@excluded_lines += node.value.nodelist.join.count("\n")
|
42
|
+
end
|
29
43
|
end
|
30
44
|
end
|
@@ -5,17 +5,5 @@ module ThemeCheck
|
|
5
5
|
class LiquidCheck < Check
|
6
6
|
extend ChecksTracking
|
7
7
|
include ParsingHelpers
|
8
|
-
|
9
|
-
# TODO: remove this once all regex checks are migrate to HtmlCheck# TODO: remove this once all regex checks are migrate to HtmlCheck
|
10
|
-
TAG = /#{Liquid::TagStart}.*?#{Liquid::TagEnd}/om
|
11
|
-
VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
|
12
|
-
START_OR_END_QUOTE = /(^['"])|(['"]$)/
|
13
|
-
QUOTED_LIQUID_ATTRIBUTE = %r{
|
14
|
-
'(?:#{TAG}|#{VARIABLE}|[^'])*'| # any combination of tag/variable or non straight quote inside straight quotes
|
15
|
-
"(?:#{TAG}|#{VARIABLE}|[^"])*" # any combination of tag/variable or non double quotes inside double quotes
|
16
|
-
}omix
|
17
|
-
ATTR = /[a-z0-9-]+/i
|
18
|
-
HTML_ATTRIBUTE = /#{ATTR}(?:=#{QUOTED_LIQUID_ATTRIBUTE})?/omix
|
19
|
-
HTML_ATTRIBUTES = /(?:#{HTML_ATTRIBUTE}|\s)*/omix
|
20
8
|
end
|
21
9
|
end
|
@@ -7,8 +7,10 @@ module ThemeCheck
|
|
7
7
|
|
8
8
|
while scanner.scan(/.*?("|')/)
|
9
9
|
yield scanner.matched[0..-2]
|
10
|
+
quote = scanner.matched[-1] == "'" ? "'" : "\""
|
10
11
|
# Skip to the end of the string
|
11
|
-
|
12
|
+
# Check for empty string first, since follow regexp uses lookahead
|
13
|
+
scanner.skip(/#{quote}/) || scanner.skip_until(/[^\\]#{quote}/)
|
12
14
|
end
|
13
15
|
|
14
16
|
yield scanner.rest if scanner.rest?
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ThemeCheck
|
4
4
|
module RegexHelpers
|
5
|
+
VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
|
6
|
+
START_OR_END_QUOTE = /(^['"])|(['"]$)/
|
5
7
|
def matches(s, re)
|
6
8
|
start_at = 0
|
7
9
|
matches = []
|
@@ -11,5 +13,20 @@ module ThemeCheck
|
|
11
13
|
end
|
12
14
|
matches
|
13
15
|
end
|
16
|
+
|
17
|
+
def href_to_file_size(href)
|
18
|
+
# asset_url (+ optional stylesheet_tag) variables
|
19
|
+
if href =~ /^#{VARIABLE}$/o && href =~ /asset_url/ && href =~ Liquid::QuotedString
|
20
|
+
asset_id = Regexp.last_match(0).gsub(START_OR_END_QUOTE, "")
|
21
|
+
asset = @theme.assets.find { |a| a.name.end_with?("/" + asset_id) }
|
22
|
+
return if asset.nil?
|
23
|
+
asset.gzipped_size
|
24
|
+
|
25
|
+
# remote URLs
|
26
|
+
elsif href =~ %r{^(https?:)?//}
|
27
|
+
asset = RemoteAssetFile.from_src(href)
|
28
|
+
asset.gzipped_size
|
29
|
+
end
|
30
|
+
end
|
14
31
|
end
|
15
32
|
end
|
data/lib/theme_check/tags.rb
CHANGED
@@ -125,6 +125,42 @@ module ThemeCheck
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
+
class Render < Liquid::Tag
|
129
|
+
SYNTAX = /((?:#{Liquid::QuotedString}|#{Liquid::VariableSegment})+)(\s+(with|#{Liquid::Render::FOR})\s+(#{Liquid::QuotedFragment}+))?(\s+(?:as)\s+(#{Liquid::VariableSegment}+))?/o
|
130
|
+
|
131
|
+
disable_tags "include"
|
132
|
+
|
133
|
+
attr_reader :template_name_expr, :attributes
|
134
|
+
|
135
|
+
def initialize(tag_name, markup, options)
|
136
|
+
super
|
137
|
+
|
138
|
+
raise SyntaxError, options[:locale].t("errors.syntax.render") unless markup =~ SYNTAX
|
139
|
+
|
140
|
+
template_name = Regexp.last_match(1)
|
141
|
+
with_or_for = Regexp.last_match(3)
|
142
|
+
variable_name = Regexp.last_match(4)
|
143
|
+
|
144
|
+
@alias_name = Regexp.last_match(6)
|
145
|
+
@variable_name_expr = variable_name ? parse_expression(variable_name) : nil
|
146
|
+
@template_name_expr = parse_expression(template_name)
|
147
|
+
@for = (with_or_for == Liquid::Render::FOR)
|
148
|
+
|
149
|
+
@attributes = {}
|
150
|
+
markup.scan(Liquid::TagAttributes) do |key, value|
|
151
|
+
@attributes[key] = parse_expression(value)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
156
|
+
def children
|
157
|
+
[
|
158
|
+
@node.template_name_expr,
|
159
|
+
] + @node.attributes.values
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
128
164
|
class Style < Liquid::Block; end
|
129
165
|
|
130
166
|
class Schema < Liquid::Raw; end
|
@@ -135,6 +171,7 @@ module ThemeCheck
|
|
135
171
|
|
136
172
|
Liquid::Template.register_tag('form', Form)
|
137
173
|
Liquid::Template.register_tag('layout', Layout)
|
174
|
+
Liquid::Template.register_tag('render', Render)
|
138
175
|
Liquid::Template.register_tag('paginate', Paginate)
|
139
176
|
Liquid::Template.register_tag('section', Section)
|
140
177
|
Liquid::Template.register_tag('style', Style)
|
data/lib/theme_check/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: theme-check
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc-André Cournoyer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-06-
|
11
|
+
date: 2021-06-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -75,11 +75,14 @@ files:
|
|
75
75
|
- docs/api/liquid_check.md
|
76
76
|
- docs/checks/TEMPLATE.md.erb
|
77
77
|
- docs/checks/asset_size_css.md
|
78
|
+
- docs/checks/asset_size_css_stylesheet_tag.md
|
78
79
|
- docs/checks/asset_size_javascript.md
|
79
80
|
- docs/checks/asset_url_filters.md
|
80
81
|
- docs/checks/content_for_header_modification.md
|
81
82
|
- docs/checks/convert_include_to_render.md
|
82
83
|
- docs/checks/default_locale.md
|
84
|
+
- docs/checks/deprecate_bgsizes.md
|
85
|
+
- docs/checks/deprecate_lazysizes.md
|
83
86
|
- docs/checks/deprecated_filter.md
|
84
87
|
- docs/checks/html_parsing_error.md
|
85
88
|
- docs/checks/img_lazy_loading.md
|
@@ -118,11 +121,14 @@ files:
|
|
118
121
|
- lib/theme_check/checks.rb
|
119
122
|
- lib/theme_check/checks/TEMPLATE.rb.erb
|
120
123
|
- lib/theme_check/checks/asset_size_css.rb
|
124
|
+
- lib/theme_check/checks/asset_size_css_stylesheet_tag.rb
|
121
125
|
- lib/theme_check/checks/asset_size_javascript.rb
|
122
126
|
- lib/theme_check/checks/asset_url_filters.rb
|
123
127
|
- lib/theme_check/checks/content_for_header_modification.rb
|
124
128
|
- lib/theme_check/checks/convert_include_to_render.rb
|
125
129
|
- lib/theme_check/checks/default_locale.rb
|
130
|
+
- lib/theme_check/checks/deprecate_bgsizes.rb
|
131
|
+
- lib/theme_check/checks/deprecate_lazysizes.rb
|
126
132
|
- lib/theme_check/checks/deprecated_filter.rb
|
127
133
|
- lib/theme_check/checks/html_parsing_error.rb
|
128
134
|
- lib/theme_check/checks/img_lazy_loading.rb
|
@@ -227,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
227
233
|
- !ruby/object:Gem::Version
|
228
234
|
version: '0'
|
229
235
|
requirements: []
|
230
|
-
rubygems_version: 3.2.
|
236
|
+
rubygems_version: 3.2.20
|
231
237
|
signing_key:
|
232
238
|
specification_version: 4
|
233
239
|
summary: A Shopify Theme Linter
|