theme-check 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/theme-check.yml +10 -3
- data/CHANGELOG.md +8 -0
- data/Gemfile +5 -5
- data/LICENSE.md +2 -0
- data/README.md +1 -0
- data/RELEASING.md +10 -3
- data/Rakefile +6 -0
- data/config/default.yml +3 -0
- data/docs/checks/remote_asset.md +82 -0
- data/lib/theme_check.rb +1 -0
- data/lib/theme_check/check.rb +1 -1
- data/lib/theme_check/checks/asset_size_css.rb +1 -1
- data/lib/theme_check/checks/asset_size_javascript.rb +1 -1
- data/lib/theme_check/checks/img_width_and_height.rb +2 -2
- data/lib/theme_check/checks/matching_translations.rb +1 -1
- data/lib/theme_check/checks/parser_blocking_javascript.rb +1 -1
- data/lib/theme_check/checks/remote_asset.rb +98 -0
- data/lib/theme_check/checks/translation_key_exists.rb +1 -4
- 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/cli.rb +13 -4
- data/lib/theme_check/disabled_checks.rb +2 -2
- data/lib/theme_check/in_memory_storage.rb +13 -16
- data/lib/theme_check/language_server/completion_engine.rb +2 -2
- data/lib/theme_check/language_server/completion_providers/filter_completion_provider.rb +1 -1
- data/lib/theme_check/language_server/completion_providers/object_completion_provider.rb +1 -1
- data/lib/theme_check/language_server/completion_providers/render_snippet_completion_provider.rb +1 -1
- data/lib/theme_check/language_server/completion_providers/tag_completion_provider.rb +2 -2
- data/lib/theme_check/language_server/document_link_engine.rb +4 -3
- data/lib/theme_check/language_server/handler.rb +29 -21
- data/lib/theme_check/language_server/server.rb +1 -2
- data/lib/theme_check/node.rb +1 -2
- data/lib/theme_check/offense.rb +1 -1
- data/lib/theme_check/releaser.rb +39 -0
- data/lib/theme_check/shopify_liquid/deprecated_filter.rb +6 -8
- data/lib/theme_check/shopify_liquid/filter.rb +3 -5
- data/lib/theme_check/shopify_liquid/object.rb +2 -6
- data/lib/theme_check/shopify_liquid/tag.rb +1 -3
- data/lib/theme_check/storage.rb +3 -3
- data/lib/theme_check/string_helpers.rb +47 -0
- data/lib/theme_check/tags.rb +1 -2
- data/lib/theme_check/theme.rb +1 -1
- data/lib/theme_check/version.rb +1 -1
- data/theme-check.gemspec +1 -2
- metadata +8 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c75935127b3ec2257d0e857c46b501a0c5189da619776202a2180234606c84ca
|
4
|
+
data.tar.gz: 3a54ffed715331b78f6ae3949b76466068539cd9fea4275e64f9f7a413ffd22b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2720180bf217fd64061f6ed4edc5ba01616fbb6ec6b30254638c992924719d179af0f2d72c4a1a1330c7b1e6a0ef9856aec4567aca73161a0cd2b59033fffcf4
|
7
|
+
data.tar.gz: c1d79dad05a55c374d4a2e008484d3c527f4483174e66ffb3c30ce96f65e8a2f03c6295de33130295b70bc63f3d3aed4dce2c7eac14de0347aa73fca599b062e
|
@@ -8,15 +8,22 @@ on:
|
|
8
8
|
|
9
9
|
jobs:
|
10
10
|
test:
|
11
|
+
runs-on: macos-latest
|
11
12
|
|
12
|
-
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
version:
|
16
|
+
- 3.0.0
|
17
|
+
- 2.7.1
|
18
|
+
|
19
|
+
name: Ruby ${{ matrix.version }}
|
13
20
|
|
14
21
|
steps:
|
15
22
|
- uses: actions/checkout@v2
|
16
|
-
- name: Set up Ruby
|
23
|
+
- name: Set up Ruby ${{ matrix.version }}
|
17
24
|
uses: ruby/setup-ruby@v1
|
18
25
|
with:
|
19
|
-
ruby-version:
|
26
|
+
ruby-version: ${{ matrix.version }}
|
20
27
|
- uses: actions/cache@v1
|
21
28
|
with:
|
22
29
|
path: vendor/bundle
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
|
2
|
+
0.7.0 / 2021-04-08
|
3
|
+
==================
|
4
|
+
|
5
|
+
* Add [RemoteAsset Check](/docs/checks/remote_asset.md)
|
6
|
+
* Fixes:
|
7
|
+
* Don't hang on self closing img tags ([#247](https://github.com/shopify/theme-check/issues/247))
|
8
|
+
* Fix document links from different root
|
9
|
+
|
2
10
|
0.6.0 / 2021-03-23
|
3
11
|
==================
|
4
12
|
|
data/Gemfile
CHANGED
@@ -21,8 +21,8 @@ group :development do
|
|
21
21
|
gem 'guard-minitest'
|
22
22
|
end
|
23
23
|
|
24
|
-
gem 'rubocop', require: false
|
25
|
-
gem 'rubocop-performance', require: false
|
26
|
-
gem 'rubocop-shopify', require: false
|
27
|
-
gem 'rubocop-minitest', require: false
|
28
|
-
gem 'rubocop-rake', require: false
|
24
|
+
gem 'rubocop', '~> 1.12.0', require: false
|
25
|
+
gem 'rubocop-performance', '~> 1.10.2', require: false
|
26
|
+
gem 'rubocop-shopify', '~> 1.0.7', require: false
|
27
|
+
gem 'rubocop-minitest', '~> 0.11.0', require: false
|
28
|
+
gem 'rubocop-rake', '~> 0.5.1', require: false
|
data/LICENSE.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
|
2
2
|
Copyright 2020-present, Shopify Inc.
|
3
3
|
|
4
|
+
Contains code from activesupport Copyright (c) 2005-2019 David Heinemeier Hansson
|
5
|
+
|
4
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
5
7
|
|
6
8
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
data/README.md
CHANGED
@@ -35,6 +35,7 @@ Theme Check currently checks for the following:
|
|
35
35
|
As well as checks that prevent easy to spot performance problems:
|
36
36
|
|
37
37
|
✅ Use of [parser-blocking](/docs/checks/parser_blocking_javascript.md) JavaScript
|
38
|
+
✅ [Use of non-Shopify domains for assets](/docs/checks/remote_asset.md)
|
38
39
|
✅ [Missing width and height attributes on `img` tags](/docs/checks/img_width_and_height.md)
|
39
40
|
✅ [Too much JavaScript](/docs/checks/asset_size_javascript.md)
|
40
41
|
✅ [Too much CSS](/docs/checks/asset_size_css.md)
|
data/RELEASING.md
CHANGED
@@ -2,11 +2,18 @@
|
|
2
2
|
|
3
3
|
1. Check the Semantic Versioning page for info on how to version the new release: http://semver.org
|
4
4
|
|
5
|
-
2.
|
5
|
+
2. Run the following command to update the version in `lib/theme_check/version.rb` and replace the `THEME_CHECK_VERSION` placeholder in the documentation for new rules:
|
6
6
|
|
7
|
-
|
7
|
+
```bash
|
8
|
+
VERSION="X.X.X"
|
9
|
+
rake prerelease[$VERSION]
|
10
|
+
```
|
11
|
+
|
12
|
+
3. Commit your changes and make a PR.
|
13
|
+
|
14
|
+
4. Merge your PR to master.
|
8
15
|
|
9
|
-
|
16
|
+
5. On [Shipit](https://shipit.shopify.io/shopify/theme-check/rubygems), deploy your commit.
|
10
17
|
|
11
18
|
## Homebrew Release Process
|
12
19
|
|
data/Rakefile
CHANGED
@@ -46,3 +46,9 @@ end
|
|
46
46
|
|
47
47
|
desc("Builds all distribution packages of the CLI")
|
48
48
|
task(package: 'package:all')
|
49
|
+
|
50
|
+
desc("Update files in the repo to match new version")
|
51
|
+
task :prerelease, [:version] do |_t, args|
|
52
|
+
require 'theme_check/releaser'
|
53
|
+
ThemeCheck::Releaser.new.release(args.version)
|
54
|
+
end
|
data/config/default.yml
CHANGED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Discourage use of third party domains for hosting assets (`RemoteAsset`)
|
2
|
+
|
3
|
+
Years ago, loading jQuery from a common CDN was good for performance because the browser cache could be reused across website. This is no longer true because browsers now include the domain from which the request was made in the cache key.
|
4
|
+
|
5
|
+
Therefore, this technique now makes things worse. Here's why:
|
6
|
+
|
7
|
+
* **The benefits of HTTP/2 prioritization are lost.** HTTP/2 prioritization is a mechanism used by servers. If different servers are used to deliver assets, there's no way to prioritize.
|
8
|
+
* **A new connection dance (DNS, TCP, TLS) must be done to start downloading the resource.** With HTTPS, this takes 5 round trips to achieve. The farther away the buyer is from that domain, the longer it takes.
|
9
|
+
* **The [slow start][slowstart] part of the Internet's TCP congestion control strategy must happen on every connection.** This means that the download "acceleration" we commonly observe must be repeated many times over.
|
10
|
+
|
11
|
+
The fix? Deliver as much as you can from a small number of connections. In a Shopify context, this is done by leveraging the `assets/` folder and the [URL filters][url_filters].
|
12
|
+
|
13
|
+
## Check Details
|
14
|
+
|
15
|
+
This check is aimed at eliminating unnecessary HTTP connections.
|
16
|
+
|
17
|
+
:-1: Examples of **incorrect** code for this check:
|
18
|
+
|
19
|
+
```liquid
|
20
|
+
<!-- Using multiple CDNs -->
|
21
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" defer></script>
|
22
|
+
{{ "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" | stylesheet_tag }}
|
23
|
+
<img src="https://example.com/heart.png" ...>
|
24
|
+
|
25
|
+
<!-- Missing img_url filter -->
|
26
|
+
<img src="{{ image }}" ...>
|
27
|
+
```
|
28
|
+
|
29
|
+
In the examples above, multiple connections are competing for resources, are accelerating download independently and are improperly prioritized.
|
30
|
+
|
31
|
+
:+1: Examples of **correct** code for this check:
|
32
|
+
|
33
|
+
```liquid
|
34
|
+
<!-- Good -->
|
35
|
+
<script src="{{ 'jquery.min.js' | asset_url }}" defer></script>
|
36
|
+
{{ 'bootstrap.min.css' | asset_url | stylesheet_tag }}
|
37
|
+
|
38
|
+
<!-- Better -->
|
39
|
+
<script src="{{ 'theme.js' | asset_url }}" defer></script>
|
40
|
+
{{ 'theme.css' | asset_url | stylesheet_tag }}
|
41
|
+
|
42
|
+
<!-- Images -->
|
43
|
+
<img src="{{ image | img_url }}" ...>
|
44
|
+
```
|
45
|
+
|
46
|
+
In the above, the JavaScript, CSS and images are all loading from the same connection. Making it so the browser and CDN can properly prioritize which assets are downloaded first while maintaining a "hot" connection that downloads fast.
|
47
|
+
|
48
|
+
This can be done by downloading the files from those CDNs directly into your theme's `assets/` folder.
|
49
|
+
|
50
|
+
Use the [`img_url` filter][img_url] for images.
|
51
|
+
|
52
|
+
## Check Options
|
53
|
+
|
54
|
+
The default configuration for this check is the following:
|
55
|
+
|
56
|
+
```yaml
|
57
|
+
RemoteAsset:
|
58
|
+
enabled: true
|
59
|
+
```
|
60
|
+
|
61
|
+
## When Not To Use It
|
62
|
+
|
63
|
+
When the remote content is highly dynamic.
|
64
|
+
|
65
|
+
## Version
|
66
|
+
|
67
|
+
This check has been introduced in Theme Check 0.7.0.
|
68
|
+
|
69
|
+
## Resources
|
70
|
+
|
71
|
+
- [Announcement by Google][googleprivacy]
|
72
|
+
- [HTTP Cache Partioning Explainer](https://github.com/shivanigithub/http-cache-partitioning)
|
73
|
+
- [Slow Start][slowstart]
|
74
|
+
- [Rule Source][codesource]
|
75
|
+
- [Documentation Source][docsource]
|
76
|
+
|
77
|
+
[googleprivacy]: https://developers.google.com/web/updates/2020/10/http-cache-partitioning#resources
|
78
|
+
[codesource]: /lib/theme_check/checks/remote_asset.rb
|
79
|
+
[docsource]: /docs/checks/remote_asset.md
|
80
|
+
[slowstart]: https://en.wikipedia.org/wiki/TCP_congestion_control#Slow_start
|
81
|
+
[url_filters]: https://shopify.dev/docs/themes/liquid/reference/filters/url-filters
|
82
|
+
[img_url]: https://shopify.dev/docs/themes/liquid/reference/filters/url-filters#img_url
|
data/lib/theme_check.rb
CHANGED
@@ -22,6 +22,7 @@ require_relative "theme_check/offense"
|
|
22
22
|
require_relative "theme_check/printer"
|
23
23
|
require_relative "theme_check/shopify_liquid"
|
24
24
|
require_relative "theme_check/storage"
|
25
|
+
require_relative "theme_check/string_helpers"
|
25
26
|
require_relative "theme_check/file_system_storage"
|
26
27
|
require_relative "theme_check/in_memory_storage"
|
27
28
|
require_relative "theme_check/tags"
|
data/lib/theme_check/check.rb
CHANGED
@@ -75,7 +75,7 @@ module ThemeCheck
|
|
75
75
|
# asset_url (+ optional stylesheet_tag) variables
|
76
76
|
if href =~ /^#{VARIABLE}$/o && href =~ /asset_url/ && href =~ Liquid::QuotedString
|
77
77
|
asset_id = Regexp.last_match(0).gsub(START_OR_END_QUOTE, "")
|
78
|
-
asset = @theme.assets.find { |a| a.name.
|
78
|
+
asset = @theme.assets.find { |a| a.name.end_with?("/" + asset_id) }
|
79
79
|
return if asset.nil?
|
80
80
|
asset.gzipped_size
|
81
81
|
|
@@ -56,7 +56,7 @@ module ThemeCheck
|
|
56
56
|
# More complicated liquid statements are not in scope.
|
57
57
|
if src =~ /^#{VARIABLE}$/o && src =~ /asset_url/ && src =~ Liquid::QuotedString
|
58
58
|
asset_id = Regexp.last_match(0).gsub(START_OR_END_QUOTE, "")
|
59
|
-
asset = @theme.assets.find { |a| a.name.
|
59
|
+
asset = @theme.assets.find { |a| a.name.end_with?("/" + asset_id) }
|
60
60
|
return if asset.nil?
|
61
61
|
asset.gzipped_size
|
62
62
|
elsif src =~ %r{^(https?:)?//}
|
@@ -8,7 +8,7 @@ module ThemeCheck
|
|
8
8
|
doc docs_url(__FILE__)
|
9
9
|
|
10
10
|
# Not implemented with lookbehinds and lookaheads because performance was shit!
|
11
|
-
IMG_TAG =
|
11
|
+
IMG_TAG = %r{<img#{HTML_ATTRIBUTES}/?>}oxim
|
12
12
|
SRC_ATTRIBUTE = /\s(src)=(#{QUOTED_LIQUID_ATTRIBUTE})/oxim
|
13
13
|
WIDTH_ATTRIBUTE = /\s(width)=(#{QUOTED_LIQUID_ATTRIBUTE})/oxim
|
14
14
|
HEIGHT_ATTRIBUTE = /\s(height)=(#{QUOTED_LIQUID_ATTRIBUTE})/oxim
|
@@ -35,7 +35,7 @@ module ThemeCheck
|
|
35
35
|
def record_missing_field_offenses(img_match)
|
36
36
|
width = WIDTH_ATTRIBUTE.match(img_match[0])
|
37
37
|
height = HEIGHT_ATTRIBUTE.match(img_match[0])
|
38
|
-
return if width
|
38
|
+
return if width && height
|
39
39
|
missing_width = width.nil?
|
40
40
|
missing_height = height.nil?
|
41
41
|
error_message = if missing_width && missing_height
|
@@ -11,7 +11,7 @@ module ThemeCheck
|
|
11
11
|
<script # Find the start of a script tag
|
12
12
|
(?=[^>]+?src=) # Make sure src= is in the script with a lookahead
|
13
13
|
(?:(?!defer|async|type=["']module['"]).)*? # Find tags that don't have defer|async|type="module"
|
14
|
-
|
14
|
+
/?>
|
15
15
|
}xim
|
16
16
|
SCRIPT_TAG_FILTER = /\{\{[^}]+script_tag\s+\}\}/
|
17
17
|
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
class RemoteAsset < LiquidCheck
|
4
|
+
include RegexHelpers
|
5
|
+
severity :suggestion
|
6
|
+
categories :liquid, :performance
|
7
|
+
doc docs_url(__FILE__)
|
8
|
+
|
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 = /<(?<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
|
30
|
+
PROTOCOL = %r{(https?:)?//}
|
31
|
+
ABSOLUTE_PATH = %r{\A/[^/]}im
|
32
|
+
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
|
+
|
39
|
+
def on_document(node)
|
40
|
+
source = node.template.source
|
41
|
+
record_html_offenses(node, source)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
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
|
51
|
+
|
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
|
56
|
+
|
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) }
|
60
|
+
end
|
61
|
+
|
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
|
+
|
80
|
+
start = match.begin(0) + resource_match.begin(:resource_url)
|
81
|
+
add_offense(
|
82
|
+
OFFENSE_MESSAGE,
|
83
|
+
node: node,
|
84
|
+
markup: resource_url,
|
85
|
+
line_number: source[0...start].count("\n") + 1,
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def non_stylesheet_link?(tag)
|
91
|
+
tag =~ REL && !(Regexp.last_match[:rel] =~ /\A['"]stylesheet['"]\Z/)
|
92
|
+
end
|
93
|
+
|
94
|
+
def url_hosted_by_shopify?(url)
|
95
|
+
url =~ /\A#{VARIABLE}\Z/oim && url =~ ASSET_URL_FILTER
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -4,10 +4,7 @@ module ThemeCheck
|
|
4
4
|
extend self
|
5
5
|
|
6
6
|
def translations
|
7
|
-
@translations ||=
|
8
|
-
# loaded as a Set because the include? lookup will be much faster.
|
9
|
-
YAML.load(File.read("#{__dir__}/../../../data/shopify_translation_keys.yml")).to_set
|
10
|
-
end
|
7
|
+
@translations ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_translation_keys.yml")).to_set
|
11
8
|
end
|
12
9
|
|
13
10
|
def include?(key)
|
data/lib/theme_check/cli.rb
CHANGED
@@ -6,14 +6,17 @@ module ThemeCheck
|
|
6
6
|
USAGE = <<~END
|
7
7
|
Usage: theme-check [options] /path/to/your/theme
|
8
8
|
|
9
|
-
Options:
|
10
|
-
--init Generate a .theme-check.yml file in the current directory
|
9
|
+
Basic Options:
|
11
10
|
-C, --config <path> Use the config provided, overriding .theme-check.yml if present
|
12
11
|
-c, --category <category> Only run this category of checks
|
13
12
|
-x, --exclude-category <category> Exclude this category of checks
|
14
|
-
-l, --list List enabled checks
|
15
13
|
-a, --auto-correct Automatically fix offenses
|
14
|
+
|
15
|
+
Miscellaneous:
|
16
|
+
--init Generate a .theme-check.yml file
|
17
|
+
--print-config Output active config to STDOUT
|
16
18
|
-h, --help Show this. Hi!
|
19
|
+
-l, --list List enabled checks
|
17
20
|
-v, --version Print Theme Check version
|
18
21
|
|
19
22
|
Description:
|
@@ -51,13 +54,15 @@ module ThemeCheck
|
|
51
54
|
auto_correct = true
|
52
55
|
when "--init"
|
53
56
|
command = :init
|
57
|
+
when "--print"
|
58
|
+
command = :print
|
54
59
|
else
|
55
60
|
@path = arg
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
59
64
|
unless [:version, :init].include?(command)
|
60
|
-
@config = if config_path
|
65
|
+
@config = if config_path
|
61
66
|
ThemeCheck::Config.new(
|
62
67
|
root: @path,
|
63
68
|
configuration: ThemeCheck::Config.load_file(config_path)
|
@@ -102,6 +107,10 @@ module ThemeCheck
|
|
102
107
|
end
|
103
108
|
end
|
104
109
|
|
110
|
+
def print
|
111
|
+
puts YAML.dump(@config.to_h)
|
112
|
+
end
|
113
|
+
|
105
114
|
def check
|
106
115
|
puts "Checking #{@config.root} ..."
|
107
116
|
storage = ThemeCheck::FileSystemStorage.new(@config.root, ignored_patterns: @config.ignored_patterns)
|
@@ -61,11 +61,11 @@ module ThemeCheck
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def start_disabling?(text)
|
64
|
-
text.strip.
|
64
|
+
text.strip.start_with?(DISABLE_START)
|
65
65
|
end
|
66
66
|
|
67
67
|
def stop_disabling?(text)
|
68
|
-
text.strip.
|
68
|
+
text.strip.start_with?(DISABLE_END)
|
69
69
|
end
|
70
70
|
|
71
71
|
# Return a list of checks from a theme-check-disable comment
|
@@ -6,32 +6,25 @@
|
|
6
6
|
# as a big hash already, leave it like that and save yourself some IO.
|
7
7
|
module ThemeCheck
|
8
8
|
class InMemoryStorage < Storage
|
9
|
-
def initialize(files = {}, root =
|
9
|
+
def initialize(files = {}, root = "/dev/null")
|
10
10
|
@files = files
|
11
|
-
@root = root
|
11
|
+
@root = Pathname.new(root)
|
12
12
|
end
|
13
13
|
|
14
|
-
def path(
|
15
|
-
|
16
|
-
name
|
14
|
+
def path(relative_path)
|
15
|
+
@root.join(relative_path)
|
17
16
|
end
|
18
17
|
|
19
|
-
def relative_path
|
20
|
-
|
21
|
-
return path.relative_path_from(@root).to_s unless path.relative? || @root.nil?
|
22
|
-
name
|
18
|
+
def read(relative_path)
|
19
|
+
@files[relative_path]
|
23
20
|
end
|
24
21
|
|
25
|
-
def
|
26
|
-
@files[relative_path
|
27
|
-
end
|
28
|
-
|
29
|
-
def write(name, content)
|
30
|
-
@files[relative_path(name)] = content
|
22
|
+
def write(relative_path, content)
|
23
|
+
@files[relative_path] = content
|
31
24
|
end
|
32
25
|
|
33
26
|
def files
|
34
|
-
@
|
27
|
+
@files.keys
|
35
28
|
end
|
36
29
|
|
37
30
|
def directories
|
@@ -41,5 +34,9 @@ module ThemeCheck
|
|
41
34
|
.map(&:to_s)
|
42
35
|
.uniq
|
43
36
|
end
|
37
|
+
|
38
|
+
def relative_path(absolute_path)
|
39
|
+
Pathname.new(absolute_path).relative_path_from(@root).to_s
|
40
|
+
end
|
44
41
|
end
|
45
42
|
end
|
@@ -10,8 +10,8 @@ module ThemeCheck
|
|
10
10
|
@providers = CompletionProvider.all.map { |x| x.new(storage) }
|
11
11
|
end
|
12
12
|
|
13
|
-
def completions(
|
14
|
-
buffer = @storage.read(
|
13
|
+
def completions(relative_path, line, col)
|
14
|
+
buffer = @storage.read(relative_path)
|
15
15
|
cursor = from_line_column_to_index(buffer, line, col)
|
16
16
|
token = find_token(buffer, cursor)
|
17
17
|
return [] if token.nil?
|
@@ -8,7 +8,7 @@ module ThemeCheck
|
|
8
8
|
def completions(content, cursor)
|
9
9
|
return [] unless can_complete?(content, cursor)
|
10
10
|
available_labels
|
11
|
-
.select { |w| w.
|
11
|
+
.select { |w| w.start_with?(partial(content, cursor)) }
|
12
12
|
.map { |filter| filter_to_completion(filter) }
|
13
13
|
end
|
14
14
|
|
@@ -7,7 +7,7 @@ module ThemeCheck
|
|
7
7
|
return [] unless can_complete?(content, cursor)
|
8
8
|
partial = first_word(content) || ''
|
9
9
|
ShopifyLiquid::Object.labels
|
10
|
-
.select { |w| w.
|
10
|
+
.select { |w| w.start_with?(partial) }
|
11
11
|
.map { |object| object_to_completion(object) }
|
12
12
|
end
|
13
13
|
|
@@ -7,12 +7,12 @@ module ThemeCheck
|
|
7
7
|
return [] unless can_complete?(content, cursor)
|
8
8
|
partial = first_word(content) || ''
|
9
9
|
ShopifyLiquid::Tag.labels
|
10
|
-
.select { |w| w.
|
10
|
+
.select { |w| w.start_with?(partial) }
|
11
11
|
.map { |tag| tag_to_completion(tag) }
|
12
12
|
end
|
13
13
|
|
14
14
|
def can_complete?(content, cursor)
|
15
|
-
content.
|
15
|
+
content.start_with?(Liquid::TagStart) && (
|
16
16
|
cursor_on_first_word?(content, cursor) ||
|
17
17
|
cursor_on_start_content?(content, cursor, Liquid::TagStart)
|
18
18
|
)
|
@@ -10,8 +10,9 @@ module ThemeCheck
|
|
10
10
|
@storage = storage
|
11
11
|
end
|
12
12
|
|
13
|
-
def document_links(
|
14
|
-
buffer = @storage.read(
|
13
|
+
def document_links(relative_path)
|
14
|
+
buffer = @storage.read(relative_path)
|
15
|
+
return [] unless buffer
|
15
16
|
matches(buffer, PARTIAL_RENDER).map do |match|
|
16
17
|
start_line, start_character = from_index_to_line_column(
|
17
18
|
buffer,
|
@@ -40,7 +41,7 @@ module ThemeCheck
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def link(partial)
|
43
|
-
|
44
|
+
"file://#{@storage.path('snippets/' + partial + '.liquid')}"
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
@@ -42,19 +42,19 @@ module ThemeCheck
|
|
42
42
|
alias_method :on_shutdown, :on_exit
|
43
43
|
|
44
44
|
def on_text_document_did_change(_id, params)
|
45
|
-
|
46
|
-
@storage.write(
|
45
|
+
relative_path = relative_path_from_text_document_uri(params)
|
46
|
+
@storage.write(relative_path, content_changes_text(params))
|
47
47
|
end
|
48
48
|
|
49
49
|
def on_text_document_did_close(_id, params)
|
50
|
-
|
51
|
-
@storage.write(
|
50
|
+
relative_path = relative_path_from_text_document_uri(params)
|
51
|
+
@storage.write(relative_path, "")
|
52
52
|
end
|
53
53
|
|
54
54
|
def on_text_document_did_open(_id, params)
|
55
|
-
|
56
|
-
@storage.write(
|
57
|
-
analyze_and_send_offenses(
|
55
|
+
relative_path = relative_path_from_text_document_uri(params)
|
56
|
+
@storage.write(relative_path, text_document_text(params))
|
57
|
+
analyze_and_send_offenses(text_document_uri(params))
|
58
58
|
end
|
59
59
|
|
60
60
|
def on_text_document_did_save(_id, params)
|
@@ -62,27 +62,27 @@ module ThemeCheck
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def on_text_document_document_link(id, params)
|
65
|
-
|
65
|
+
relative_path = relative_path_from_text_document_uri(params)
|
66
66
|
send_response(
|
67
67
|
id: id,
|
68
|
-
result: document_links(
|
68
|
+
result: document_links(relative_path)
|
69
69
|
)
|
70
70
|
end
|
71
71
|
|
72
72
|
def on_text_document_completion(id, params)
|
73
|
-
|
73
|
+
relative_path = relative_path_from_text_document_uri(params)
|
74
74
|
line = params.dig('position', 'line')
|
75
75
|
col = params.dig('position', 'character')
|
76
76
|
send_response(
|
77
77
|
id: id,
|
78
|
-
result: completions(
|
78
|
+
result: completions(relative_path, line, col)
|
79
79
|
)
|
80
80
|
end
|
81
81
|
|
82
82
|
private
|
83
83
|
|
84
84
|
def in_memory_storage(root)
|
85
|
-
config =
|
85
|
+
config = config_for_path(root)
|
86
86
|
|
87
87
|
# Make a real FS to get the files from the snippets folder
|
88
88
|
fs = ThemeCheck::FileSystemStorage.new(
|
@@ -95,13 +95,17 @@ module ThemeCheck
|
|
95
95
|
.map { |fn| [fn, ""] }
|
96
96
|
.to_h
|
97
97
|
|
98
|
-
InMemoryStorage.new(files, root)
|
98
|
+
InMemoryStorage.new(files, config.root)
|
99
99
|
end
|
100
100
|
|
101
101
|
def text_document_uri(params)
|
102
102
|
params.dig('textDocument', 'uri').sub('file://', '')
|
103
103
|
end
|
104
104
|
|
105
|
+
def relative_path_from_text_document_uri(params)
|
106
|
+
@storage.relative_path(text_document_uri(params))
|
107
|
+
end
|
108
|
+
|
105
109
|
def text_document_text(params)
|
106
110
|
params.dig('textDocument', 'text')
|
107
111
|
end
|
@@ -110,9 +114,13 @@ module ThemeCheck
|
|
110
114
|
params.dig('contentChanges', 0, 'text')
|
111
115
|
end
|
112
116
|
|
113
|
-
def
|
114
|
-
root = ThemeCheck::Config.find(
|
115
|
-
|
117
|
+
def config_for_path(path)
|
118
|
+
root = ThemeCheck::Config.find(path) || @root_path
|
119
|
+
ThemeCheck::Config.from_path(root)
|
120
|
+
end
|
121
|
+
|
122
|
+
def analyze_and_send_offenses(absolute_path)
|
123
|
+
config = config_for_path(absolute_path)
|
116
124
|
storage = ThemeCheck::FileSystemStorage.new(
|
117
125
|
config.root,
|
118
126
|
ignored_patterns: config.ignored_patterns
|
@@ -131,12 +139,12 @@ module ThemeCheck
|
|
131
139
|
analyzer.offenses
|
132
140
|
end
|
133
141
|
|
134
|
-
def completions(
|
135
|
-
@completion_engine.completions(
|
142
|
+
def completions(relative_path, line, col)
|
143
|
+
@completion_engine.completions(relative_path, line, col)
|
136
144
|
end
|
137
145
|
|
138
|
-
def document_links(
|
139
|
-
@document_link_engine.document_links(
|
146
|
+
def document_links(relative_path)
|
147
|
+
@document_link_engine.document_links(relative_path)
|
140
148
|
end
|
141
149
|
|
142
150
|
def send_diagnostics(offenses)
|
@@ -162,7 +170,7 @@ module ThemeCheck
|
|
162
170
|
send_response(
|
163
171
|
method: 'textDocument/publishDiagnostics',
|
164
172
|
params: {
|
165
|
-
uri: "file
|
173
|
+
uri: "file://#{path}",
|
166
174
|
diagnostics: offenses.map { |offense| offense_to_diagnostic(offense) },
|
167
175
|
},
|
168
176
|
)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'json'
|
3
3
|
require 'stringio'
|
4
|
-
require 'active_support/core_ext/string/inflections'
|
5
4
|
|
6
5
|
module ThemeCheck
|
7
6
|
module LanguageServer
|
@@ -99,7 +98,7 @@ module ThemeCheck
|
|
99
98
|
end
|
100
99
|
|
101
100
|
def to_snake_case(method_name)
|
102
|
-
method_name.gsub(/[^\w]/, '_')
|
101
|
+
StringHelpers.underscore(method_name.gsub(/[^\w]/, '_'))
|
103
102
|
end
|
104
103
|
|
105
104
|
def initial_line
|
data/lib/theme_check/node.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'active_support/core_ext/string/inflections'
|
3
2
|
|
4
3
|
module ThemeCheck
|
5
4
|
# A node from the Liquid AST, the result of parsing a template.
|
@@ -101,7 +100,7 @@ module ThemeCheck
|
|
101
100
|
# The `:under_score_name` of this type of node. Used to dispatch to the `on_<type_name>`
|
102
101
|
# and `after_<type_name>` check methods.
|
103
102
|
def type_name
|
104
|
-
@type_name ||= @value.class.name.
|
103
|
+
@type_name ||= StringHelpers.underscore(StringHelpers.demodulize(@value.class.name)).to_sym
|
105
104
|
end
|
106
105
|
|
107
106
|
# Is this node inside a `{% liquid ... %}` block?
|
data/lib/theme_check/offense.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'theme_check/version'
|
3
|
+
|
4
|
+
module ThemeCheck
|
5
|
+
class Releaser
|
6
|
+
ROOT = File.expand_path('../../..', __FILE__)
|
7
|
+
LIB = File.join(ROOT, 'lib')
|
8
|
+
|
9
|
+
class VersionError < StandardError; end
|
10
|
+
|
11
|
+
def release(version)
|
12
|
+
raise VersionError, "Missing version argument." if version.nil?
|
13
|
+
raise VersionError, "Version should be a string." unless version.is_a?(String)
|
14
|
+
raise VersionError, "Version should be a valid semver version." unless version =~ /^\d+\.\d+.\d+$/
|
15
|
+
update_docs(version)
|
16
|
+
update_version(version)
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_version(version)
|
20
|
+
version_file_path = File.join(LIB, 'theme_check/version.rb')
|
21
|
+
version_file = File.read(version_file_path)
|
22
|
+
updated_version_file = version_file.gsub(ThemeCheck::VERSION, version)
|
23
|
+
|
24
|
+
return if updated_version_file == version_file
|
25
|
+
puts "Updating version to #{version} in #{version_file_path}."
|
26
|
+
File.write(version_file_path, updated_version_file)
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_docs(version)
|
30
|
+
Dir[ROOT + '/docs/checks/*.md'].each do |filename|
|
31
|
+
doc_content = File.read(filename)
|
32
|
+
updated_doc_content = doc_content.gsub('THEME_CHECK_VERSION', version)
|
33
|
+
next if updated_doc_content == doc_content
|
34
|
+
puts "Replacing `THEME_CHECK_VERSION` with #{version} in #{Pathname.new(filename).relative_path_from(ROOT)}"
|
35
|
+
File.write(filename, updated_doc_content)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -17,14 +17,12 @@ module ThemeCheck
|
|
17
17
|
private
|
18
18
|
|
19
19
|
def all
|
20
|
-
@all ||=
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
20
|
+
@all ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/deprecated_filters.yml"))
|
21
|
+
.values
|
22
|
+
.each_with_object({}) do |filters, acc|
|
23
|
+
filters.each do |(filter, alternatives)|
|
24
|
+
acc[filter] = alternatives
|
25
|
+
end
|
28
26
|
end
|
29
27
|
end
|
30
28
|
end
|
@@ -7,11 +7,9 @@ module ThemeCheck
|
|
7
7
|
extend self
|
8
8
|
|
9
9
|
def labels
|
10
|
-
@labels ||=
|
11
|
-
|
12
|
-
|
13
|
-
.flatten
|
14
|
-
end
|
10
|
+
@labels ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/filters.yml"))
|
11
|
+
.values
|
12
|
+
.flatten
|
15
13
|
end
|
16
14
|
end
|
17
15
|
end
|
@@ -7,15 +7,11 @@ module ThemeCheck
|
|
7
7
|
extend self
|
8
8
|
|
9
9
|
def labels
|
10
|
-
@labels ||=
|
11
|
-
YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/objects.yml"))
|
12
|
-
end
|
10
|
+
@labels ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/objects.yml"))
|
13
11
|
end
|
14
12
|
|
15
13
|
def plus_labels
|
16
|
-
@plus_labels ||=
|
17
|
-
YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/plus_objects.yml"))
|
18
|
-
end
|
14
|
+
@plus_labels ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/plus_objects.yml"))
|
19
15
|
end
|
20
16
|
end
|
21
17
|
end
|
data/lib/theme_check/storage.rb
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
module ThemeCheck
|
4
4
|
class Storage
|
5
|
-
def
|
5
|
+
def path(relative_path)
|
6
6
|
raise NotImplementedError
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
9
|
+
def read(relative_path)
|
10
10
|
raise NotImplementedError
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def write(relative_path, content)
|
14
14
|
raise NotImplementedError
|
15
15
|
end
|
16
16
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThemeCheck
|
4
|
+
module StringHelpers
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Removes the module part from the expression in the string.
|
8
|
+
# Ported from ActiveSupport
|
9
|
+
#
|
10
|
+
# demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
|
11
|
+
# demodulize('Inflections') # => "Inflections"
|
12
|
+
# demodulize('::Inflections') # => "Inflections"
|
13
|
+
# demodulize('') # => ""
|
14
|
+
#
|
15
|
+
# See also #deconstantize.
|
16
|
+
def demodulize(path)
|
17
|
+
path = path.to_s
|
18
|
+
if (i = path.rindex("::"))
|
19
|
+
path[(i + 2)..-1]
|
20
|
+
else
|
21
|
+
path
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Makes an underscored, lowercase form from the expression in the string.
|
26
|
+
# Base on ActiveSupport's
|
27
|
+
#
|
28
|
+
# Changes '::' to '/' to convert namespaces to paths.
|
29
|
+
#
|
30
|
+
# underscore('ActiveModel') # => "active_model"
|
31
|
+
# underscore('ActiveModel::Errors') # => "active_model/errors"
|
32
|
+
#
|
33
|
+
# As a rule of thumb you can think of +underscore+ as the inverse of
|
34
|
+
# #camelize, though there are cases where that does not hold:
|
35
|
+
#
|
36
|
+
# camelize(underscore('SSLError')) # => "SslError"
|
37
|
+
def underscore(camel_cased_word)
|
38
|
+
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
|
39
|
+
word = camel_cased_word.to_s.gsub("::", "/")
|
40
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
41
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
42
|
+
word.tr!("-", "_")
|
43
|
+
word.downcase!
|
44
|
+
word
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/theme_check/tags.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "active_support/core_ext/string/starts_ends_with"
|
3
2
|
|
4
3
|
module ThemeCheck
|
5
4
|
module Tags
|
@@ -19,7 +18,7 @@ module ThemeCheck
|
|
19
18
|
"Error in tag 'section' - Valid syntax: section '[type]'",
|
20
19
|
) unless match
|
21
20
|
@section_name = match[:section_name].tr(%('"), '')
|
22
|
-
@section_name.chomp!(".liquid") if @section_name.
|
21
|
+
@section_name.chomp!(".liquid") if @section_name.end_with?(".liquid")
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
data/lib/theme_check/theme.rb
CHANGED
data/lib/theme_check/version.rb
CHANGED
data/theme-check.gemspec
CHANGED
@@ -22,7 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
-
spec.add_dependency('liquid', '>= 5')
|
26
|
-
spec.add_dependency('activesupport')
|
25
|
+
spec.add_dependency('liquid', '>= 5.0.1')
|
27
26
|
spec.add_dependency('nokogumbo')
|
28
27
|
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.
|
4
|
+
version: 0.7.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-
|
11
|
+
date: 2021-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -16,28 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.0.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: activesupport
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
26
|
+
version: 5.0.1
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: nokogumbo
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,6 +84,7 @@ files:
|
|
98
84
|
- docs/checks/missing_template.md
|
99
85
|
- docs/checks/nested_snippet.md
|
100
86
|
- docs/checks/parser_blocking_javascript.md
|
87
|
+
- docs/checks/remote_asset.md
|
101
88
|
- docs/checks/required_directories.md
|
102
89
|
- docs/checks/required_layout_theme_object.md
|
103
90
|
- docs/checks/space_inside_braces.md
|
@@ -133,6 +120,7 @@ files:
|
|
133
120
|
- lib/theme_check/checks/missing_template.rb
|
134
121
|
- lib/theme_check/checks/nested_snippet.rb
|
135
122
|
- lib/theme_check/checks/parser_blocking_javascript.rb
|
123
|
+
- lib/theme_check/checks/remote_asset.rb
|
136
124
|
- lib/theme_check/checks/required_directories.rb
|
137
125
|
- lib/theme_check/checks/required_layout_theme_object.rb
|
138
126
|
- lib/theme_check/checks/space_inside_braces.rb
|
@@ -179,6 +167,7 @@ files:
|
|
179
167
|
- lib/theme_check/parsing_helpers.rb
|
180
168
|
- lib/theme_check/printer.rb
|
181
169
|
- lib/theme_check/regex_helpers.rb
|
170
|
+
- lib/theme_check/releaser.rb
|
182
171
|
- lib/theme_check/remote_asset_file.rb
|
183
172
|
- lib/theme_check/shopify_liquid.rb
|
184
173
|
- lib/theme_check/shopify_liquid/deprecated_filter.rb
|
@@ -186,6 +175,7 @@ files:
|
|
186
175
|
- lib/theme_check/shopify_liquid/object.rb
|
187
176
|
- lib/theme_check/shopify_liquid/tag.rb
|
188
177
|
- lib/theme_check/storage.rb
|
178
|
+
- lib/theme_check/string_helpers.rb
|
189
179
|
- lib/theme_check/tags.rb
|
190
180
|
- lib/theme_check/template.rb
|
191
181
|
- lib/theme_check/theme.rb
|