theme-check 0.9.0 → 0.9.1
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 +6 -0
- data/config/default.yml +4 -0
- data/docs/checks/asset_url_filters.md +56 -0
- data/lib/theme_check/checks/asset_url_filters.rb +46 -0
- data/lib/theme_check/checks/remote_asset.rb +21 -79
- data/lib/theme_check/html_node.rb +4 -0
- data/lib/theme_check/html_visitor.rb +2 -0
- data/lib/theme_check/liquid_check.rb +1 -0
- data/lib/theme_check/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a114e025e51ad2a09395b4ae0dca33e9417942a76ed781fdd639e7d7d1b35928
|
4
|
+
data.tar.gz: ff3c7f9b43f5ccae262cd22c8547d308a71e214cdd7f924208c25122624a48dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e4ead0f1d63cc68dfbaff6dd5462f08b9008c04dd87fae9499875cf967e501c68327adec0ac3c1f253bbdbf1343d14ddeb63fe58b4c9f4cb24f494315898d70
|
7
|
+
data.tar.gz: 62baa63bcf203d2bd0a7894787011f23d363cb6192920ac67c2ce0dfbe89f382c461a3d5a989c23234e5e1a9db7bfd9d10c4801bca121c1993a082b58887705d
|
data/CHANGELOG.md
CHANGED
data/config/default.yml
CHANGED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Ensure `asset_url` filters are used when serving assets (`AssetUrlFilters`)
|
2
|
+
|
3
|
+
See the [`RemoteAsset` check documentation][remote_asset] for a detailed explanation on why remote assets are discouraged.
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This check is aimed at eliminating unnecessary HTTP connections.
|
8
|
+
|
9
|
+
:-1: Examples of **incorrect** code for this check:
|
10
|
+
|
11
|
+
```liquid
|
12
|
+
<!-- Using multiple CDNs -->
|
13
|
+
{{ "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" | stylesheet_tag }}
|
14
|
+
|
15
|
+
<!-- Missing img_url filter -->
|
16
|
+
{{ url | img_tag }}
|
17
|
+
```
|
18
|
+
|
19
|
+
:+1: Examples of **correct** code for this check:
|
20
|
+
|
21
|
+
```liquid
|
22
|
+
{{ 'bootstrap.min.css' | asset_url | stylesheet_tag }}
|
23
|
+
|
24
|
+
<!-- Images -->
|
25
|
+
{{ url | img_url | img_tag }}
|
26
|
+
```
|
27
|
+
|
28
|
+
Use the [`assets_url`](asset_url) or [`img_url`](img_url) filter to load the files in your theme's `assets/` folder from the Shopify CDN.
|
29
|
+
|
30
|
+
## Check Options
|
31
|
+
|
32
|
+
The default configuration for this check is the following:
|
33
|
+
|
34
|
+
```yaml
|
35
|
+
AssetUrlFilters:
|
36
|
+
enabled: true
|
37
|
+
```
|
38
|
+
|
39
|
+
## When Not To Use It
|
40
|
+
|
41
|
+
When the remote content is highly dynamic.
|
42
|
+
|
43
|
+
## Version
|
44
|
+
|
45
|
+
This check has been introduced in Theme Check 0.9.1.
|
46
|
+
|
47
|
+
## Resources
|
48
|
+
|
49
|
+
- [Rule Source][codesource]
|
50
|
+
- [Documentation Source][docsource]
|
51
|
+
|
52
|
+
[codesource]: /lib/theme_check/checks/remote_asset_filters.rb
|
53
|
+
[docsource]: /docs/checks/remote_asset_filters.md
|
54
|
+
[remote_asset]: /docs/checks/remote_asset.md
|
55
|
+
[asset_url]: https://shopify.dev/docs/themes/liquid/reference/filters/url-filters#assert_url
|
56
|
+
[img_url]: https://shopify.dev/docs/themes/liquid/reference/filters/url-filters#img_url
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
class AssetUrlFilters < LiquidCheck
|
4
|
+
severity :suggestion
|
5
|
+
categories :liquid, :performance
|
6
|
+
doc docs_url(__FILE__)
|
7
|
+
|
8
|
+
HTML_FILTERS = [
|
9
|
+
'stylesheet_tag',
|
10
|
+
'script_tag',
|
11
|
+
'img_tag',
|
12
|
+
]
|
13
|
+
ASSET_URL_FILTERS = [
|
14
|
+
'asset_url',
|
15
|
+
'asset_img_url',
|
16
|
+
'file_img_url',
|
17
|
+
'file_url',
|
18
|
+
'global_asset_url',
|
19
|
+
'img_url',
|
20
|
+
'payment_type_img_url',
|
21
|
+
'shopify_asset_url',
|
22
|
+
]
|
23
|
+
|
24
|
+
def on_variable(node)
|
25
|
+
record_variable_offense(node)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def record_variable_offense(variable_node)
|
31
|
+
# We flag HTML tags with URLs not hosted by Shopify
|
32
|
+
return if !html_resource_drop?(variable_node) || variable_hosted_by_shopify?(variable_node)
|
33
|
+
add_offense("Use one of the asset_url filters to serve assets", node: variable_node)
|
34
|
+
end
|
35
|
+
|
36
|
+
def html_resource_drop?(variable_node)
|
37
|
+
variable_node.value.filters
|
38
|
+
.any? { |(filter_name, *_filter_args)| HTML_FILTERS.include?(filter_name) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def variable_hosted_by_shopify?(variable_node)
|
42
|
+
variable_node.value.filters
|
43
|
+
.any? { |(filter_name, *_filter_args)| ASSET_URL_FILTERS.include?(filter_name) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,99 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module ThemeCheck
|
3
|
-
class RemoteAsset <
|
4
|
-
include RegexHelpers
|
3
|
+
class RemoteAsset < HtmlCheck
|
5
4
|
severity :suggestion
|
6
|
-
categories :
|
5
|
+
categories :html, :performance
|
7
6
|
doc docs_url(__FILE__)
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
HTML_FILTERS = [
|
12
|
-
'stylesheet_tag',
|
13
|
-
'script_tag',
|
14
|
-
'img_tag',
|
15
|
-
]
|
16
|
-
ASSET_URL_FILTERS = [
|
17
|
-
'asset_url',
|
18
|
-
'asset_img_url',
|
19
|
-
'file_img_url',
|
20
|
-
'file_url',
|
21
|
-
'global_asset_url',
|
22
|
-
'img_url',
|
23
|
-
'payment_type_img_url',
|
24
|
-
'shopify_asset_url',
|
25
|
-
]
|
26
|
-
|
27
|
-
RESOURCE_TAG = %r{<(?<tag_name>img|script|link|source)#{HTML_ATTRIBUTES}/?>}oim
|
28
|
-
RESOURCE_URL = /\s(?:src|href)=(?<resource_url>#{QUOTED_LIQUID_ATTRIBUTE})/oim
|
29
|
-
ASSET_URL_FILTER = /[\|\s]*(#{ASSET_URL_FILTERS.join('|')})/omi
|
8
|
+
TAGS = %w[img script link source]
|
30
9
|
PROTOCOL = %r{(https?:)?//}
|
31
10
|
ABSOLUTE_PATH = %r{\A/[^/]}im
|
32
11
|
RELATIVE_PATH = %r{\A(?!#{PROTOCOL})[^/\{]}oim
|
33
|
-
REL = /\srel=(?<rel>#{QUOTED_LIQUID_ATTRIBUTE})/oim
|
34
|
-
|
35
|
-
def on_variable(node)
|
36
|
-
record_variable_offense(node)
|
37
|
-
end
|
38
12
|
|
39
|
-
def
|
40
|
-
|
41
|
-
record_html_offenses(node, source)
|
42
|
-
end
|
13
|
+
def on_element(node)
|
14
|
+
return unless TAGS.include?(node.name)
|
43
15
|
|
44
|
-
|
16
|
+
resource_url = node.attributes["src"]&.value || node.attributes["href"]&.value
|
17
|
+
return if resource_url.nil? || resource_url.empty?
|
45
18
|
|
46
|
-
|
47
|
-
|
48
|
-
return if
|
49
|
-
|
50
|
-
end
|
19
|
+
# Ignore if URL is Liquid, taken care of by AssetUrlFilters check
|
20
|
+
return if resource_url =~ ABSOLUTE_PATH
|
21
|
+
return if resource_url =~ RELATIVE_PATH
|
22
|
+
return if url_hosted_by_shopify?(resource_url)
|
51
23
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
24
|
+
# Ignore non-stylesheet rel tags
|
25
|
+
rel = node.attributes["rel"]
|
26
|
+
return if rel && rel.value != "stylesheet"
|
56
27
|
|
57
|
-
|
58
|
-
|
59
|
-
|
28
|
+
add_offense(
|
29
|
+
"Asset should be served by the Shopify CDN for better performance.",
|
30
|
+
node: node,
|
31
|
+
)
|
60
32
|
end
|
61
33
|
|
62
|
-
|
63
|
-
# HTML AST. We have to resort to looking at the HTML with regexes
|
64
|
-
# to figure out if we have a resource (stylesheet, script, or media)
|
65
|
-
# that points to a remote domain.
|
66
|
-
def record_html_offenses(node, source)
|
67
|
-
matches(source, RESOURCE_TAG).each do |match|
|
68
|
-
tag = match[0]
|
69
|
-
|
70
|
-
# We don't flag stuff without URLs
|
71
|
-
next unless tag =~ RESOURCE_URL
|
72
|
-
resource_match = Regexp.last_match
|
73
|
-
resource_url = resource_match[:resource_url].gsub(START_OR_END_QUOTE, '')
|
74
|
-
|
75
|
-
next if non_stylesheet_link?(tag)
|
76
|
-
next if url_hosted_by_shopify?(resource_url)
|
77
|
-
next if resource_url =~ ABSOLUTE_PATH
|
78
|
-
next if resource_url =~ RELATIVE_PATH
|
79
|
-
next if resource_url.empty?
|
80
|
-
|
81
|
-
start = match.begin(0) + resource_match.begin(:resource_url)
|
82
|
-
add_offense(
|
83
|
-
OFFENSE_MESSAGE,
|
84
|
-
node: node,
|
85
|
-
markup: resource_url,
|
86
|
-
line_number: source[0...start].count("\n") + 1,
|
87
|
-
)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def non_stylesheet_link?(tag)
|
92
|
-
tag =~ REL && !(Regexp.last_match[:rel] =~ /\A['"]stylesheet['"]\Z/)
|
93
|
-
end
|
34
|
+
private
|
94
35
|
|
95
36
|
def url_hosted_by_shopify?(url)
|
96
|
-
url
|
37
|
+
url.start_with?(Liquid::VariableStart) &&
|
38
|
+
AssetUrlFilters::ASSET_URL_FILTERS.any? { |filter| url.include?(filter) }
|
97
39
|
end
|
98
40
|
end
|
99
41
|
end
|
@@ -22,10 +22,12 @@ module ThemeCheck
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def visit(node)
|
25
|
+
call_checks(:on_element, node) if node.element?
|
25
26
|
call_checks(:"on_#{node.name}", node)
|
26
27
|
node.children.each { |child| visit(child) }
|
27
28
|
unless node.literal?
|
28
29
|
call_checks(:"after_#{node.name}", node)
|
30
|
+
call_checks(:after_element, node) if node.element?
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
@@ -6,6 +6,7 @@ module ThemeCheck
|
|
6
6
|
extend ChecksTracking
|
7
7
|
include ParsingHelpers
|
8
8
|
|
9
|
+
# TODO: remove this once all regex checks are migrate to HtmlCheck# TODO: remove this once all regex checks are migrate to HtmlCheck
|
9
10
|
TAG = /#{Liquid::TagStart}.*?#{Liquid::TagEnd}/om
|
10
11
|
VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
|
11
12
|
START_OR_END_QUOTE = /(^['"])|(['"]$)/
|
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.9.
|
4
|
+
version: 0.9.1
|
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-
|
11
|
+
date: 2021-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- docs/checks/CHECK_DOCS_TEMPLATE.md
|
73
73
|
- docs/checks/asset_size_css.md
|
74
74
|
- docs/checks/asset_size_javascript.md
|
75
|
+
- docs/checks/asset_url_filters.md
|
75
76
|
- docs/checks/content_for_header_modification.md
|
76
77
|
- docs/checks/convert_include_to_render.md
|
77
78
|
- docs/checks/default_locale.md
|
@@ -111,6 +112,7 @@ files:
|
|
111
112
|
- lib/theme_check/checks.rb
|
112
113
|
- lib/theme_check/checks/asset_size_css.rb
|
113
114
|
- lib/theme_check/checks/asset_size_javascript.rb
|
115
|
+
- lib/theme_check/checks/asset_url_filters.rb
|
114
116
|
- lib/theme_check/checks/content_for_header_modification.rb
|
115
117
|
- lib/theme_check/checks/convert_include_to_render.rb
|
116
118
|
- lib/theme_check/checks/default_locale.rb
|