theme-check 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5970832846bda5a0e7cb27c99ebef32382050c911350d1d208211849bef7d55
4
- data.tar.gz: 89d3bb2d5a261c817dddfbf50ad9ac31a589f88fe4f2a2df23b8c5aaad51c4db
3
+ metadata.gz: a114e025e51ad2a09395b4ae0dca33e9417942a76ed781fdd639e7d7d1b35928
4
+ data.tar.gz: ff3c7f9b43f5ccae262cd22c8547d308a71e214cdd7f924208c25122624a48dc
5
5
  SHA512:
6
- metadata.gz: 2ee4f610dc461c6e70e870723f801ddb0f1e5381eebfc6622030af68486c2c6650c11e530be38456cf0df8666ecc5e78b26d16cf60428142e04f0ebde45edf9f
7
- data.tar.gz: 36053d9337a817da4334a5341be1877f04d86b3f4d88606a2acccb2028c0a3a07227086183de5aa06bd44e9beeebd0a4650d08b746fdfbced854e945469d61a3
6
+ metadata.gz: 3e4ead0f1d63cc68dfbaff6dd5462f08b9008c04dd87fae9499875cf967e501c68327adec0ac3c1f253bbdbf1343d14ddeb63fe58b4c9f4cb24f494315898d70
7
+ data.tar.gz: 62baa63bcf203d2bd0a7894787011f23d363cb6192920ac67c2ce0dfbe89f382c461a3d5a989c23234e5e1a9db7bfd9d10c4801bca121c1993a082b58887705d
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
1
 
2
+ v0.9.1 / 2021-06-04
3
+ ==================
4
+
5
+ * Convert `RemoteAsset` into an `HtmlCheck`
6
+ * Move Liquid logic from `RemoteAsset` to a new `AssetUrlFilters` check
7
+
2
8
  v0.9.0 / 2021-05-28
3
9
  ==================
4
10
 
data/config/default.yml CHANGED
@@ -130,6 +130,10 @@ RemoteAsset:
130
130
  enabled: true
131
131
  ignore: []
132
132
 
133
+ AssetUrlFilters:
134
+ enabled: true
135
+ ignore: []
136
+
133
137
  ContentForHeaderModification:
134
138
  enabled: true
135
139
  ignore: []
@@ -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 < LiquidCheck
4
- include RegexHelpers
3
+ class RemoteAsset < HtmlCheck
5
4
  severity :suggestion
6
- categories :liquid, :performance
5
+ categories :html, :performance
7
6
  doc docs_url(__FILE__)
8
7
 
9
- OFFENSE_MESSAGE = "Asset should be served by the Shopify CDN for better performance."
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 on_document(node)
40
- source = node.template.source
41
- record_html_offenses(node, source)
42
- end
13
+ def on_element(node)
14
+ return unless TAGS.include?(node.name)
43
15
 
44
- private
16
+ resource_url = node.attributes["src"]&.value || node.attributes["href"]&.value
17
+ return if resource_url.nil? || resource_url.empty?
45
18
 
46
- def record_variable_offense(variable_node)
47
- # We flag HTML tags with URLs not hosted by Shopify
48
- return if !html_resource_drop?(variable_node) || variable_hosted_by_shopify?(variable_node)
49
- add_offense(OFFENSE_MESSAGE, node: variable_node)
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
- def html_resource_drop?(variable_node)
53
- variable_node.value.filters
54
- .any? { |(filter_name, *_filter_args)| HTML_FILTERS.include?(filter_name) }
55
- end
24
+ # Ignore non-stylesheet rel tags
25
+ rel = node.attributes["rel"]
26
+ return if rel && rel.value != "stylesheet"
56
27
 
57
- def variable_hosted_by_shopify?(variable_node)
58
- variable_node.value.filters
59
- .any? { |(filter_name, *_filter_args)| ASSET_URL_FILTERS.include?(filter_name) }
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
- # This part is slightly more complicated because we don't have an
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 =~ /\A#{VARIABLE}\Z/oim && url =~ ASSET_URL_FILTER
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
@@ -17,6 +17,10 @@ module ThemeCheck
17
17
  @value.name == "text"
18
18
  end
19
19
 
20
+ def element?
21
+ @value.element?
22
+ end
23
+
20
24
  def children
21
25
  @value.children.map { |child| HtmlNode.new(child, template) }
22
26
  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 = /(^['"])|(['"]$)/
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
- VERSION = "0.9.0"
3
+ VERSION = "0.9.1"
4
4
  end
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.0
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-05-28 00:00:00.000000000 Z
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