theme-check 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +39 -0
- data/config/default.yml +16 -0
- data/config/nothing.yml +11 -0
- data/config/theme_app_extension.yml +153 -0
- data/docs/checks/asset_size_app_block_css.md +52 -0
- data/docs/checks/asset_size_app_block_javascript.md +57 -0
- data/lib/theme_check.rb +15 -0
- data/lib/theme_check/analyzer.rb +25 -21
- data/lib/theme_check/asset_file.rb +3 -15
- data/lib/theme_check/bug.rb +3 -1
- data/lib/theme_check/check.rb +23 -1
- 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/cli.rb +34 -13
- data/lib/theme_check/config.rb +56 -10
- data/lib/theme_check/exceptions.rb +29 -27
- data/lib/theme_check/json_file.rb +2 -29
- data/lib/theme_check/language_server/constants.rb +8 -0
- data/lib/theme_check/language_server/document_link_engine.rb +40 -4
- data/lib/theme_check/tags.rb +26 -9
- data/lib/theme_check/template.rb +3 -32
- data/lib/theme_check/theme_file.rb +40 -0
- data/lib/theme_check/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: adc4aaf3260408a9b52d9afaa59662308f173a5d5dcd0f3663cbcc68da1d7f39
|
4
|
+
data.tar.gz: 50cd7e0d42fda99ad3816a846c25868c38b92ea521e0be759c515548dd82f70b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ffad3310e441edd678143de233584a09167ffff1e09b96109cef417d360d797821dc9ef3491af4564608cd3b35aa59be41ebea6bb76030e1544d53bc9db8d02
|
7
|
+
data.tar.gz: 30b6d8f3b58b95c833a78bc30ceea6cfcfa35d563e3d184b6aee51817965be87b352c2dbad5ff67a74f2a64479fe1945c11db738c1bfb6e64f610ae15b862cb3
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,17 @@
|
|
1
1
|
|
2
|
+
v1.1.0 / 2021-07-06
|
3
|
+
==================
|
4
|
+
|
5
|
+
* Add `--fail-level` CLI flag to configure exit code
|
6
|
+
* Refactor all theme file classes to inherit from `ThemeFile`
|
7
|
+
* Fix `undefined method liquid?` error when scanning from LSP
|
8
|
+
* Adding asset document links
|
9
|
+
* Allow initializing theme app extension configuration files
|
10
|
+
* Allow disabling registering mock Liquid tags w/ `ThemeCheck::Tags.register_tags = false`
|
11
|
+
* Support Theme App Extensions
|
12
|
+
* Add checks for theme app extension block JS/CSS
|
13
|
+
* Disable Liquid::C when parsing Liquid templates
|
14
|
+
|
2
15
|
v1.0.0 / 2021-06-28
|
3
16
|
==================
|
4
17
|
|
data/README.md
CHANGED
@@ -100,6 +100,8 @@ TemplateLength:
|
|
100
100
|
# Or ignore certain paths
|
101
101
|
ignore:
|
102
102
|
- snippets/icon-*
|
103
|
+
# Or change the severity (error|suggestion|style)
|
104
|
+
severity: suggestion
|
103
105
|
|
104
106
|
# Enable a custom check
|
105
107
|
MyCustomCheck
|
@@ -141,3 +143,40 @@ Disable checks for the _entire document_ by placing the comment on the first lin
|
|
141
143
|
|
142
144
|
{%assign x = 1%}
|
143
145
|
```
|
146
|
+
|
147
|
+
## Exit Code and `--fail-level`
|
148
|
+
|
149
|
+
Use the `--fail-level` (default: `error`) flag to configure the exit code of theme-check. Useful in CI scenarios.
|
150
|
+
|
151
|
+
Example:
|
152
|
+
|
153
|
+
```
|
154
|
+
# Make CI fail on styles warnings, suggestions, and errors
|
155
|
+
theme-check --fail-level style path_to_theme
|
156
|
+
|
157
|
+
# Make CI fail on suggestions, and errors
|
158
|
+
theme-check --fail-level suggestion path_to_theme
|
159
|
+
|
160
|
+
# Make CI fail on errors
|
161
|
+
theme-check path_to_theme
|
162
|
+
```
|
163
|
+
|
164
|
+
There are three fail levels:
|
165
|
+
|
166
|
+
- `error`
|
167
|
+
- `suggestion`
|
168
|
+
- `style`
|
169
|
+
|
170
|
+
Exit code meanings:
|
171
|
+
|
172
|
+
- 0: Success!
|
173
|
+
- 1: Your code doesn't pass the checks
|
174
|
+
- 2: There's a bug in theme-check
|
175
|
+
|
176
|
+
If you would like to change the severity of a check, you can do so with the `severity` attribute. Example:
|
177
|
+
|
178
|
+
```yaml
|
179
|
+
DeprecateLazysizes:
|
180
|
+
enabled: true
|
181
|
+
severity: error
|
182
|
+
```
|
data/config/default.yml
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
root: .
|
2
2
|
|
3
|
+
extends: :nothing
|
4
|
+
|
3
5
|
require: []
|
4
6
|
|
7
|
+
include_categories: []
|
8
|
+
|
9
|
+
exclude_categories: []
|
10
|
+
|
5
11
|
ignore:
|
6
12
|
- node_modules/*
|
7
13
|
|
@@ -161,3 +167,13 @@ ImgLazyLoading:
|
|
161
167
|
HtmlParsingError:
|
162
168
|
enabled: true
|
163
169
|
ignore: []
|
170
|
+
|
171
|
+
AssetSizeAppBlockJavaScript:
|
172
|
+
enabled: false
|
173
|
+
ignore: []
|
174
|
+
threshold_in_bytes: 10_000
|
175
|
+
|
176
|
+
AssetSizeAppBlockCSS:
|
177
|
+
enabled: false
|
178
|
+
ignore: []
|
179
|
+
threshold_in_bytes: 100_000
|
data/config/nothing.yml
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
root: .
|
2
|
+
|
3
|
+
extends: :nothing
|
4
|
+
|
5
|
+
require: []
|
6
|
+
|
7
|
+
include_categories: []
|
8
|
+
|
9
|
+
exclude_categories: []
|
10
|
+
|
11
|
+
ignore:
|
12
|
+
- node_modules/*
|
13
|
+
|
14
|
+
ConvertIncludeToRender:
|
15
|
+
enabled: true
|
16
|
+
ignore: []
|
17
|
+
|
18
|
+
LiquidTag:
|
19
|
+
enabled: true
|
20
|
+
ignore: []
|
21
|
+
min_consecutive_statements: 4
|
22
|
+
|
23
|
+
MissingTemplate:
|
24
|
+
enabled: true
|
25
|
+
ignore: []
|
26
|
+
|
27
|
+
NestedSnippet:
|
28
|
+
enabled: true
|
29
|
+
ignore: []
|
30
|
+
max_nesting_level: 3
|
31
|
+
|
32
|
+
RequiredLayoutThemeObject:
|
33
|
+
enabled: true
|
34
|
+
ignore: []
|
35
|
+
|
36
|
+
SpaceInsideBraces:
|
37
|
+
enabled: true
|
38
|
+
ignore: []
|
39
|
+
|
40
|
+
SyntaxError:
|
41
|
+
enabled: true
|
42
|
+
ignore: []
|
43
|
+
|
44
|
+
TemplateLength:
|
45
|
+
enabled: true
|
46
|
+
ignore: []
|
47
|
+
max_length: 200
|
48
|
+
# Exclude content of {% schema %} in line count
|
49
|
+
exclude_schema: true
|
50
|
+
|
51
|
+
UnknownFilter:
|
52
|
+
enabled: true
|
53
|
+
ignore: []
|
54
|
+
|
55
|
+
UnusedAssign:
|
56
|
+
enabled: true
|
57
|
+
ignore: []
|
58
|
+
|
59
|
+
UnusedSnippet:
|
60
|
+
enabled: true
|
61
|
+
ignore: []
|
62
|
+
|
63
|
+
MatchingSchemaTranslations:
|
64
|
+
enabled: true
|
65
|
+
ignore: []
|
66
|
+
|
67
|
+
MatchingTranslations:
|
68
|
+
enabled: true
|
69
|
+
ignore: []
|
70
|
+
|
71
|
+
TranslationKeyExists:
|
72
|
+
enabled: true
|
73
|
+
ignore: []
|
74
|
+
|
75
|
+
ValidHTMLTranslation:
|
76
|
+
enabled: true
|
77
|
+
ignore: []
|
78
|
+
|
79
|
+
ValidJson:
|
80
|
+
enabled: true
|
81
|
+
ignore: []
|
82
|
+
|
83
|
+
ValidSchema:
|
84
|
+
enabled: true
|
85
|
+
ignore: []
|
86
|
+
|
87
|
+
UndefinedObject:
|
88
|
+
enabled: true
|
89
|
+
ignore: []
|
90
|
+
exclude_snippets: true
|
91
|
+
|
92
|
+
RequiredDirectories:
|
93
|
+
enabled: false
|
94
|
+
ignore: []
|
95
|
+
|
96
|
+
DeprecatedFilter:
|
97
|
+
enabled: true
|
98
|
+
ignore: []
|
99
|
+
|
100
|
+
MissingEnableComment:
|
101
|
+
enabled: true
|
102
|
+
ignore: []
|
103
|
+
|
104
|
+
ParserBlockingJavaScript:
|
105
|
+
enabled: true
|
106
|
+
ignore: []
|
107
|
+
|
108
|
+
ParserBlockingScriptTag:
|
109
|
+
enabled: true
|
110
|
+
|
111
|
+
AssetSizeJavaScript:
|
112
|
+
enabled: true
|
113
|
+
ignore: []
|
114
|
+
threshold_in_bytes: 10_000
|
115
|
+
|
116
|
+
AssetSizeCSS:
|
117
|
+
enabled: true
|
118
|
+
ignore: []
|
119
|
+
threshold_in_bytes: 100_000
|
120
|
+
|
121
|
+
ImgWidthAndHeight:
|
122
|
+
enabled: true
|
123
|
+
ignore: []
|
124
|
+
|
125
|
+
RemoteAsset:
|
126
|
+
enabled: true
|
127
|
+
ignore: []
|
128
|
+
|
129
|
+
AssetUrlFilters:
|
130
|
+
enabled: true
|
131
|
+
ignore: []
|
132
|
+
|
133
|
+
ContentForHeaderModification:
|
134
|
+
enabled: true
|
135
|
+
ignore: []
|
136
|
+
|
137
|
+
ImgLazyLoading:
|
138
|
+
enabled: true
|
139
|
+
ignore: []
|
140
|
+
|
141
|
+
HtmlParsingError:
|
142
|
+
enabled: true
|
143
|
+
ignore: []
|
144
|
+
|
145
|
+
AssetSizeAppBlockJavaScript:
|
146
|
+
enabled: true
|
147
|
+
ignore: []
|
148
|
+
threshold_in_bytes: 10_000
|
149
|
+
|
150
|
+
AssetSizeAppBlockCSS:
|
151
|
+
enabled: true
|
152
|
+
ignore: []
|
153
|
+
threshold_in_bytes: 100_000
|
@@ -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: false
|
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
|
data/lib/theme_check.rb
CHANGED
@@ -4,6 +4,7 @@ require "liquid"
|
|
4
4
|
require_relative "theme_check/version"
|
5
5
|
require_relative "theme_check/bug"
|
6
6
|
require_relative "theme_check/exceptions"
|
7
|
+
require_relative "theme_check/theme_file"
|
7
8
|
require_relative "theme_check/analyzer"
|
8
9
|
require_relative "theme_check/check"
|
9
10
|
require_relative "theme_check/checks_tracking"
|
@@ -45,3 +46,17 @@ Dir[__dir__ + "/theme_check/checks/*.rb"].each { |file| require file }
|
|
45
46
|
# UTF-8 is the default internal and external encoding, like in Rails & Shopify.
|
46
47
|
Encoding.default_external = Encoding::UTF_8
|
47
48
|
Encoding.default_internal = Encoding::UTF_8
|
49
|
+
|
50
|
+
module ThemeCheck
|
51
|
+
def self.with_liquid_c_disabled
|
52
|
+
if defined?(Liquid::C)
|
53
|
+
was_enabled = Liquid::C.enabled
|
54
|
+
Liquid::C.enabled = false if was_enabled
|
55
|
+
end
|
56
|
+
yield
|
57
|
+
ensure
|
58
|
+
if defined?(Liquid::C) && was_enabled
|
59
|
+
Liquid::C.enabled = true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
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
|
|
@@ -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?
|
@@ -85,7 +96,18 @@ module ThemeCheck
|
|
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
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
# Reports errors when too much CSS is being referenced from a Theme App
|
4
|
+
# Extension block
|
5
|
+
class AssetSizeAppBlockCSS < LiquidCheck
|
6
|
+
severity :error
|
7
|
+
category :performance
|
8
|
+
doc docs_url(__FILE__)
|
9
|
+
|
10
|
+
# Don't allow this check to be disabled with a comment,
|
11
|
+
# since we need to be able to enforce this server-side
|
12
|
+
can_disable false
|
13
|
+
|
14
|
+
attr_reader :threshold_in_bytes
|
15
|
+
|
16
|
+
def initialize(threshold_in_bytes: 100_000)
|
17
|
+
@threshold_in_bytes = threshold_in_bytes
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_schema(node)
|
21
|
+
schema = JSON.parse(node.value.nodelist.join)
|
22
|
+
|
23
|
+
if (stylesheet = schema["stylesheet"])
|
24
|
+
size = asset_size(stylesheet)
|
25
|
+
if size && size > threshold_in_bytes
|
26
|
+
add_offense(
|
27
|
+
"CSS in Theme App Extension blocks exceeds compressed size threshold (#{threshold_in_bytes} Bytes)",
|
28
|
+
node: node
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
rescue JSON::ParserError
|
33
|
+
# Ignored, handled in ValidSchema.
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def asset_size(name)
|
39
|
+
asset = @theme["assets/#{name}"]
|
40
|
+
return if asset.nil?
|
41
|
+
asset.gzipped_size
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
# Reports errors when too much JS is being referenced from a Theme App
|
4
|
+
# Extension block
|
5
|
+
class AssetSizeAppBlockJavaScript < LiquidCheck
|
6
|
+
severity :error
|
7
|
+
category :performance
|
8
|
+
doc docs_url(__FILE__)
|
9
|
+
|
10
|
+
# Don't allow this check to be disabled with a comment,
|
11
|
+
# since we need to be able to enforce this server-side
|
12
|
+
can_disable false
|
13
|
+
|
14
|
+
attr_reader :threshold_in_bytes
|
15
|
+
|
16
|
+
def initialize(threshold_in_bytes: 10_000)
|
17
|
+
@threshold_in_bytes = threshold_in_bytes
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_schema(node)
|
21
|
+
schema = JSON.parse(node.value.nodelist.join)
|
22
|
+
|
23
|
+
if (javascript = schema["javascript"])
|
24
|
+
size = asset_size(javascript)
|
25
|
+
if size && size > threshold_in_bytes
|
26
|
+
add_offense(
|
27
|
+
"JavaScript in Theme App Extension blocks exceeds compressed size threshold (#{threshold_in_bytes} Bytes)",
|
28
|
+
node: node
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
rescue JSON::ParserError
|
33
|
+
# Ignored, handled in ValidSchema.
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def asset_size(name)
|
39
|
+
asset = @theme["assets/#{name}"]
|
40
|
+
return if asset.nil?
|
41
|
+
asset.gzipped_size
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/theme_check/cli.rb
CHANGED
@@ -10,10 +10,11 @@ module ThemeCheck
|
|
10
10
|
def initialize
|
11
11
|
@path = "."
|
12
12
|
@command = :check
|
13
|
-
@
|
13
|
+
@include_categories = []
|
14
14
|
@exclude_categories = []
|
15
15
|
@auto_correct = false
|
16
16
|
@config_path = nil
|
17
|
+
@fail_level = :error
|
17
18
|
end
|
18
19
|
|
19
20
|
def option_parser(parser = OptionParser.new, help: true)
|
@@ -25,20 +26,27 @@ module ThemeCheck
|
|
25
26
|
@option_parser.separator("Basic Options:")
|
26
27
|
@option_parser.on(
|
27
28
|
"-C", "--config PATH",
|
28
|
-
"Use the config provided, overriding .theme-check.yml if present"
|
29
|
+
"Use the config provided, overriding .theme-check.yml if present",
|
30
|
+
"Use :theme_app_extension to use default checks for theme app extensions"
|
29
31
|
) { |path| @config_path = path }
|
30
32
|
@option_parser.on(
|
31
|
-
"-c", "--category CATEGORY",
|
32
|
-
"
|
33
|
-
) { |category| @
|
33
|
+
"-c", "--category CATEGORY", Check::CATEGORIES, "Only run this category of checks",
|
34
|
+
"Runs checks matching all categories when specified more than once"
|
35
|
+
) { |category| @include_categories << category.to_sym }
|
34
36
|
@option_parser.on(
|
35
|
-
"-x", "--exclude-category CATEGORY",
|
36
|
-
"
|
37
|
+
"-x", "--exclude-category CATEGORY", Check::CATEGORIES, "Exclude this category of checks",
|
38
|
+
"Excludes checks matching any category when specified more than once"
|
37
39
|
) { |category| @exclude_categories << category.to_sym }
|
38
40
|
@option_parser.on(
|
39
41
|
"-a", "--auto-correct",
|
40
42
|
"Automatically fix offenses"
|
41
43
|
) { @auto_correct = true }
|
44
|
+
@option_parser.on(
|
45
|
+
"--fail-level SEVERITY", Check::SEVERITIES,
|
46
|
+
"Minimum severity (error|suggestion|style) for exit with error code"
|
47
|
+
) do |severity|
|
48
|
+
@fail_level = severity.to_sym
|
49
|
+
end
|
42
50
|
|
43
51
|
@option_parser.separator("")
|
44
52
|
@option_parser.separator("Miscellaneous:")
|
@@ -77,6 +85,8 @@ module ThemeCheck
|
|
77
85
|
|
78
86
|
def parse(argv)
|
79
87
|
@path = option_parser.parse(argv).first || "."
|
88
|
+
rescue OptionParser::InvalidArgument => e
|
89
|
+
abort(e.message)
|
80
90
|
end
|
81
91
|
|
82
92
|
def run!
|
@@ -84,13 +94,13 @@ module ThemeCheck
|
|
84
94
|
@config = if @config_path
|
85
95
|
ThemeCheck::Config.new(
|
86
96
|
root: @path,
|
87
|
-
configuration: ThemeCheck::Config.
|
97
|
+
configuration: ThemeCheck::Config.load_config(@config_path)
|
88
98
|
)
|
89
99
|
else
|
90
100
|
ThemeCheck::Config.from_path(@path)
|
91
101
|
end
|
92
|
-
@config.
|
93
|
-
@config.exclude_categories = @exclude_categories
|
102
|
+
@config.include_categories = @include_categories unless @include_categories.empty?
|
103
|
+
@config.exclude_categories = @exclude_categories unless @exclude_categories.empty?
|
94
104
|
@config.auto_correct = @auto_correct
|
95
105
|
end
|
96
106
|
|
@@ -99,12 +109,16 @@ module ThemeCheck
|
|
99
109
|
|
100
110
|
def run
|
101
111
|
run!
|
112
|
+
exit(0)
|
102
113
|
rescue Abort => e
|
103
114
|
if e.message.empty?
|
104
115
|
exit(1)
|
105
116
|
else
|
106
117
|
abort(e.message)
|
107
118
|
end
|
119
|
+
rescue ThemeCheckError => e
|
120
|
+
STDERR.puts(e.message)
|
121
|
+
exit(2)
|
108
122
|
end
|
109
123
|
|
110
124
|
def self.parse_and_run!(argv)
|
@@ -130,11 +144,16 @@ module ThemeCheck
|
|
130
144
|
def init
|
131
145
|
dotfile_path = ThemeCheck::Config.find(@path)
|
132
146
|
if dotfile_path.nil?
|
133
|
-
|
147
|
+
config_name = if @config_path && @config_path[0] == ":"
|
148
|
+
"#{@config_path[1..]}.yml"
|
149
|
+
else
|
150
|
+
"default.yml"
|
151
|
+
end
|
152
|
+
File.write(File.join(@path, ThemeCheck::Config::DOTFILE), File.read(ThemeCheck::Config.bundled_config_path(config_name)))
|
134
153
|
|
135
154
|
puts "Writing new #{ThemeCheck::Config::DOTFILE} to #{@path}"
|
136
155
|
else
|
137
|
-
raise Abort, "#{ThemeCheck::Config::DOTFILE} already exists at #{@path}
|
156
|
+
raise Abort, "#{ThemeCheck::Config::DOTFILE} already exists at #{@path}"
|
138
157
|
end
|
139
158
|
end
|
140
159
|
|
@@ -157,7 +176,9 @@ module ThemeCheck
|
|
157
176
|
analyzer.analyze_theme
|
158
177
|
analyzer.correct_offenses
|
159
178
|
ThemeCheck::Printer.new.print(theme, analyzer.offenses, @config.auto_correct)
|
160
|
-
raise Abort, "" if analyzer.uncorrectable_offenses.any?
|
179
|
+
raise Abort, "" if analyzer.uncorrectable_offenses.any? do |offense|
|
180
|
+
offense.check.severity_value <= Check.severity_value(@fail_level)
|
181
|
+
end
|
161
182
|
end
|
162
183
|
end
|
163
184
|
end
|
data/lib/theme_check/config.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
module ThemeCheck
|
4
4
|
class Config
|
5
5
|
DOTFILE = '.theme-check.yml'
|
6
|
-
|
6
|
+
BUNDLED_CONFIGS_DIR = "#{__dir__}/../../config"
|
7
7
|
BOOLEAN = [true, false]
|
8
8
|
|
9
9
|
attr_reader :root
|
10
|
-
attr_accessor :
|
10
|
+
attr_accessor :auto_correct
|
11
11
|
|
12
12
|
class << self
|
13
13
|
attr_reader :last_loaded_config
|
@@ -42,18 +42,46 @@ module ThemeCheck
|
|
42
42
|
YAML.load_file(absolute_path)
|
43
43
|
end
|
44
44
|
|
45
|
+
def bundled_config_path(name)
|
46
|
+
"#{BUNDLED_CONFIGS_DIR}/#{name}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_bundled_config(name)
|
50
|
+
load_file(bundled_config_path(name))
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_config(path)
|
54
|
+
if path[0] == ":"
|
55
|
+
load_bundled_config("#{path[1..]}.yml")
|
56
|
+
elsif path.is_a?(Symbol)
|
57
|
+
load_bundled_config("#{path}.yml")
|
58
|
+
else
|
59
|
+
load_file(path)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
45
63
|
def default
|
46
|
-
@default ||=
|
64
|
+
@default ||= load_config(":default")
|
47
65
|
end
|
48
66
|
end
|
49
67
|
|
50
68
|
def initialize(root: nil, configuration: nil, should_resolve_requires: true)
|
51
69
|
@configuration = if configuration
|
70
|
+
# TODO: Do we need to handle extends here? What base configuration
|
71
|
+
# should we validate against once Theme App Extensions has its own
|
72
|
+
# checks? :all?
|
52
73
|
validate_configuration(configuration)
|
53
74
|
else
|
54
75
|
{}
|
55
76
|
end
|
56
|
-
|
77
|
+
|
78
|
+
# Follow extends
|
79
|
+
extends = @configuration["extends"] || ":default"
|
80
|
+
while extends
|
81
|
+
extended_configuration = self.class.load_config(extends)
|
82
|
+
extends = extended_configuration["extends"]
|
83
|
+
@configuration = merge_configurations!(@configuration, extended_configuration)
|
84
|
+
end
|
57
85
|
|
58
86
|
@root = if root && @configuration.key?("root")
|
59
87
|
Pathname.new(root).join(@configuration["root"])
|
@@ -61,8 +89,6 @@ module ThemeCheck
|
|
61
89
|
Pathname.new(root)
|
62
90
|
end
|
63
91
|
|
64
|
-
@only_categories = []
|
65
|
-
@exclude_categories = []
|
66
92
|
@auto_correct = false
|
67
93
|
|
68
94
|
resolve_requires if @root && should_resolve_requires
|
@@ -87,16 +113,18 @@ module ThemeCheck
|
|
87
113
|
check_class = ThemeCheck.const_get(check_name)
|
88
114
|
|
89
115
|
next if check_class.categories.any? { |category| exclude_categories.include?(category) }
|
90
|
-
next if
|
116
|
+
next if include_categories.any? && !include_categories.all? { |category| check_class.categories.include?(category) }
|
91
117
|
|
92
118
|
options_for_check = options.transform_keys(&:to_sym)
|
93
119
|
options_for_check.delete(:enabled)
|
120
|
+
severity = options_for_check.delete(:severity)
|
94
121
|
ignored_patterns = options_for_check.delete(:ignore) || []
|
95
122
|
check = if options_for_check.empty?
|
96
123
|
check_class.new
|
97
124
|
else
|
98
125
|
check_class.new(**options_for_check)
|
99
126
|
end
|
127
|
+
check.severity = severity.to_sym if severity
|
100
128
|
check.ignored_patterns = ignored_patterns
|
101
129
|
check.options = options_for_check
|
102
130
|
check
|
@@ -107,6 +135,22 @@ module ThemeCheck
|
|
107
135
|
self["ignore"] || []
|
108
136
|
end
|
109
137
|
|
138
|
+
def include_categories
|
139
|
+
self["include_categories"] || []
|
140
|
+
end
|
141
|
+
|
142
|
+
def include_categories=(categories)
|
143
|
+
@configuration["include_categories"] = categories
|
144
|
+
end
|
145
|
+
|
146
|
+
def exclude_categories
|
147
|
+
self["exclude_categories"] || []
|
148
|
+
end
|
149
|
+
|
150
|
+
def exclude_categories=(categories)
|
151
|
+
@configuration["exclude_categories"] = categories
|
152
|
+
end
|
153
|
+
|
110
154
|
private
|
111
155
|
|
112
156
|
def check_name?(name)
|
@@ -133,6 +177,8 @@ module ThemeCheck
|
|
133
177
|
else
|
134
178
|
warn("bad configuration type for #{name}: expected a Hash, got #{value.inspect}")
|
135
179
|
end
|
180
|
+
elsif key == "severity"
|
181
|
+
valid_configuration[key] = value
|
136
182
|
elsif default.nil?
|
137
183
|
warn("unknown configuration: #{name}")
|
138
184
|
elsif BOOLEAN.include?(default) && !BOOLEAN.include?(value)
|
@@ -147,13 +193,13 @@ module ThemeCheck
|
|
147
193
|
valid_configuration
|
148
194
|
end
|
149
195
|
|
150
|
-
def
|
151
|
-
|
196
|
+
def merge_configurations!(configuration, extended_configuration)
|
197
|
+
extended_configuration.each do |key, default|
|
152
198
|
value = configuration[key]
|
153
199
|
|
154
200
|
case value
|
155
201
|
when Hash
|
156
|
-
|
202
|
+
merge_configurations!(value, default)
|
157
203
|
when nil
|
158
204
|
configuration[key] = default
|
159
205
|
end
|
@@ -1,32 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "net/http"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
module ThemeCheck
|
5
|
+
TIMEOUT_EXCEPTIONS = [
|
6
|
+
Net::ReadTimeout,
|
7
|
+
Net::OpenTimeout,
|
8
|
+
Net::WriteTimeout,
|
9
|
+
Errno::ETIMEDOUT,
|
10
|
+
Timeout::Error,
|
11
|
+
]
|
11
12
|
|
12
|
-
CONNECTION_EXCEPTIONS = [
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
]
|
13
|
+
CONNECTION_EXCEPTIONS = [
|
14
|
+
IOError,
|
15
|
+
EOFError,
|
16
|
+
SocketError,
|
17
|
+
Errno::EINVAL,
|
18
|
+
Errno::ECONNRESET,
|
19
|
+
Errno::ECONNABORTED,
|
20
|
+
Errno::EPIPE,
|
21
|
+
Errno::ECONNREFUSED,
|
22
|
+
Errno::EAGAIN,
|
23
|
+
Errno::EHOSTUNREACH,
|
24
|
+
Errno::ENETUNREACH,
|
25
|
+
]
|
25
26
|
|
26
|
-
NET_HTTP_EXCEPTIONS = [
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
]
|
27
|
+
NET_HTTP_EXCEPTIONS = [
|
28
|
+
Net::HTTPBadResponse,
|
29
|
+
Net::HTTPHeaderSyntaxError,
|
30
|
+
Net::ProtocolError,
|
31
|
+
*TIMEOUT_EXCEPTIONS,
|
32
|
+
*CONNECTION_EXCEPTIONS,
|
33
|
+
]
|
34
|
+
end
|
@@ -1,29 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "json"
|
3
|
-
require "pathname"
|
4
3
|
|
5
4
|
module ThemeCheck
|
6
|
-
class JsonFile
|
5
|
+
class JsonFile < ThemeFile
|
7
6
|
def initialize(relative_path, storage)
|
8
|
-
|
9
|
-
@storage = storage
|
7
|
+
super
|
10
8
|
@loaded = false
|
11
9
|
@content = nil
|
12
10
|
@parser_error = nil
|
13
11
|
end
|
14
12
|
|
15
|
-
def path
|
16
|
-
@storage.path(@relative_path)
|
17
|
-
end
|
18
|
-
|
19
|
-
def relative_path
|
20
|
-
@relative_pathname ||= Pathname.new(@relative_path)
|
21
|
-
end
|
22
|
-
|
23
|
-
def source
|
24
|
-
@source ||= @storage.read(@relative_path)
|
25
|
-
end
|
26
|
-
|
27
13
|
def content
|
28
14
|
load!
|
29
15
|
@content
|
@@ -34,23 +20,10 @@ module ThemeCheck
|
|
34
20
|
@parser_error
|
35
21
|
end
|
36
22
|
|
37
|
-
def name
|
38
|
-
relative_path.sub_ext('').to_s
|
39
|
-
end
|
40
|
-
|
41
23
|
def json?
|
42
24
|
true
|
43
25
|
end
|
44
26
|
|
45
|
-
def liquid?
|
46
|
-
false
|
47
|
-
end
|
48
|
-
|
49
|
-
def ==(other)
|
50
|
-
other.is_a?(JsonFile) && relative_path == other.relative_path
|
51
|
-
end
|
52
|
-
alias_method :eql?, :==
|
53
|
-
|
54
27
|
private
|
55
28
|
|
56
29
|
def load!
|
@@ -10,5 +10,13 @@ module ThemeCheck
|
|
10
10
|
^\s*render\s+'(?<partial>[^']*)'|
|
11
11
|
^\s*render\s+"(?<partial>[^"]*)"
|
12
12
|
}mix
|
13
|
+
ASSET_INCLUDE = %r{
|
14
|
+
\{\%-?\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
|
15
|
+
\{\%-?\s*"(?<partial>[^"]*)"\s*\|\s*asset_url|
|
16
|
+
|
17
|
+
# in liquid tags the whole line is white space until the asset partial
|
18
|
+
^\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
|
19
|
+
^\s*"(?<partial>[^"]*)"\s*\|\s*asset_url
|
20
|
+
}mix
|
13
21
|
end
|
14
22
|
end
|
@@ -13,7 +13,7 @@ module ThemeCheck
|
|
13
13
|
def document_links(relative_path)
|
14
14
|
buffer = @storage.read(relative_path)
|
15
15
|
return [] unless buffer
|
16
|
-
matches(buffer, PARTIAL_RENDER).map do |match|
|
16
|
+
snippet_matches = matches(buffer, PARTIAL_RENDER).map do |match|
|
17
17
|
start_line, start_character = from_index_to_row_column(
|
18
18
|
buffer,
|
19
19
|
match.begin(:partial),
|
@@ -25,7 +25,7 @@ module ThemeCheck
|
|
25
25
|
)
|
26
26
|
|
27
27
|
{
|
28
|
-
target:
|
28
|
+
target: snippet_link(match[:partial]),
|
29
29
|
range: {
|
30
30
|
start: {
|
31
31
|
line: start_line,
|
@@ -38,10 +38,46 @@ module ThemeCheck
|
|
38
38
|
},
|
39
39
|
}
|
40
40
|
end
|
41
|
+
asset_matches = matches(buffer, ASSET_INCLUDE).map do |match|
|
42
|
+
start_line, start_character = from_index_to_row_column(
|
43
|
+
buffer,
|
44
|
+
match.begin(:partial),
|
45
|
+
)
|
46
|
+
|
47
|
+
end_line, end_character = from_index_to_row_column(
|
48
|
+
buffer,
|
49
|
+
match.end(:partial)
|
50
|
+
)
|
51
|
+
|
52
|
+
{
|
53
|
+
target: asset_link(match[:partial]),
|
54
|
+
range: {
|
55
|
+
start: {
|
56
|
+
line: start_line,
|
57
|
+
character: start_character,
|
58
|
+
},
|
59
|
+
end: {
|
60
|
+
line: end_line,
|
61
|
+
character: end_character,
|
62
|
+
},
|
63
|
+
},
|
64
|
+
}
|
65
|
+
end
|
66
|
+
snippet_matches + asset_matches
|
67
|
+
end
|
68
|
+
|
69
|
+
def snippet_link(partial)
|
70
|
+
file_link('snippets', partial, '.liquid')
|
41
71
|
end
|
42
72
|
|
43
|
-
def
|
44
|
-
|
73
|
+
def asset_link(partial)
|
74
|
+
file_link('assets', partial, '')
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def file_link(directory, partial, extension)
|
80
|
+
"file://#{@storage.path(directory + '/' + partial + extension)}"
|
45
81
|
end
|
46
82
|
end
|
47
83
|
end
|
data/lib/theme_check/tags.rb
CHANGED
@@ -169,14 +169,31 @@ module ThemeCheck
|
|
169
169
|
|
170
170
|
class Stylesheet < Liquid::Raw; end
|
171
171
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
172
|
+
class << self
|
173
|
+
attr_writer :register_tags
|
174
|
+
|
175
|
+
def register_tags?
|
176
|
+
@register_tags
|
177
|
+
end
|
178
|
+
|
179
|
+
def register_tag(name, klass)
|
180
|
+
Liquid::Template.register_tag(name, klass)
|
181
|
+
end
|
182
|
+
|
183
|
+
def register_tags!
|
184
|
+
return if !register_tags? || (defined?(@registered_tags) && @registered_tags)
|
185
|
+
@registered_tags = true
|
186
|
+
register_tag('form', Form)
|
187
|
+
register_tag('layout', Layout)
|
188
|
+
register_tag('render', Render)
|
189
|
+
register_tag('paginate', Paginate)
|
190
|
+
register_tag('section', Section)
|
191
|
+
register_tag('style', Style)
|
192
|
+
register_tag('schema', Schema)
|
193
|
+
register_tag('javascript', Javascript)
|
194
|
+
register_tag('stylesheet', Stylesheet)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
self.register_tags = true
|
181
198
|
end
|
182
199
|
end
|
data/lib/theme_check/template.rb
CHANGED
@@ -1,25 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "pathname"
|
3
2
|
|
4
3
|
module ThemeCheck
|
5
|
-
class Template
|
6
|
-
def initialize(relative_path, storage)
|
7
|
-
@storage = storage
|
8
|
-
@relative_path = relative_path
|
9
|
-
end
|
10
|
-
|
11
|
-
def path
|
12
|
-
@storage.path(@relative_path)
|
13
|
-
end
|
14
|
-
|
15
|
-
def relative_path
|
16
|
-
@relative_pathname ||= Pathname.new(@relative_path)
|
17
|
-
end
|
18
|
-
|
19
|
-
def source
|
20
|
-
@source ||= @storage.read(@relative_path)
|
21
|
-
end
|
22
|
-
|
4
|
+
class Template < ThemeFile
|
23
5
|
def write
|
24
6
|
content = updated_content
|
25
7
|
if source != content
|
@@ -28,14 +10,6 @@ module ThemeCheck
|
|
28
10
|
end
|
29
11
|
end
|
30
12
|
|
31
|
-
def name
|
32
|
-
relative_path.sub_ext('').to_s
|
33
|
-
end
|
34
|
-
|
35
|
-
def json?
|
36
|
-
false
|
37
|
-
end
|
38
|
-
|
39
13
|
def liquid?
|
40
14
|
true
|
41
15
|
end
|
@@ -88,16 +62,13 @@ module ThemeCheck
|
|
88
62
|
parse.root
|
89
63
|
end
|
90
64
|
|
91
|
-
def ==(other)
|
92
|
-
other.is_a?(Template) && relative_path == other.relative_path
|
93
|
-
end
|
94
|
-
alias_method :eql?, :==
|
95
|
-
|
96
65
|
def self.parse(source)
|
66
|
+
Tags.register_tags!
|
97
67
|
Liquid::Template.parse(
|
98
68
|
source,
|
99
69
|
line_numbers: true,
|
100
70
|
error_mode: :warn,
|
71
|
+
disable_liquid_c_nodes: true,
|
101
72
|
)
|
102
73
|
end
|
103
74
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
module ThemeCheck
|
5
|
+
class ThemeFile
|
6
|
+
def initialize(relative_path, storage)
|
7
|
+
@relative_path = relative_path
|
8
|
+
@storage = storage
|
9
|
+
end
|
10
|
+
|
11
|
+
def path
|
12
|
+
@storage.path(@relative_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def relative_path
|
16
|
+
@relative_pathname ||= Pathname.new(@relative_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def name
|
20
|
+
relative_path.sub_ext('').to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def source
|
24
|
+
@source ||= @storage.read(@relative_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def json?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def liquid?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
other.is_a?(self.class) && relative_path == other.relative_path
|
37
|
+
end
|
38
|
+
alias_method :eql?, :==
|
39
|
+
end
|
40
|
+
end
|
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: 1.
|
4
|
+
version: 1.1.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-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -62,6 +62,8 @@ files:
|
|
62
62
|
- Rakefile
|
63
63
|
- bin/liquid-server
|
64
64
|
- config/default.yml
|
65
|
+
- config/nothing.yml
|
66
|
+
- config/theme_app_extension.yml
|
65
67
|
- data/shopify_liquid/deprecated_filters.yml
|
66
68
|
- data/shopify_liquid/filters.yml
|
67
69
|
- data/shopify_liquid/objects.yml
|
@@ -74,6 +76,8 @@ files:
|
|
74
76
|
- docs/api/json_check.md
|
75
77
|
- docs/api/liquid_check.md
|
76
78
|
- docs/checks/TEMPLATE.md.erb
|
79
|
+
- docs/checks/asset_size_app_block_css.md
|
80
|
+
- docs/checks/asset_size_app_block_javascript.md
|
77
81
|
- docs/checks/asset_size_css.md
|
78
82
|
- docs/checks/asset_size_css_stylesheet_tag.md
|
79
83
|
- docs/checks/asset_size_javascript.md
|
@@ -120,6 +124,8 @@ files:
|
|
120
124
|
- lib/theme_check/check.rb
|
121
125
|
- lib/theme_check/checks.rb
|
122
126
|
- lib/theme_check/checks/TEMPLATE.rb.erb
|
127
|
+
- lib/theme_check/checks/asset_size_app_block_css.rb
|
128
|
+
- lib/theme_check/checks/asset_size_app_block_javascript.rb
|
123
129
|
- lib/theme_check/checks/asset_size_css.rb
|
124
130
|
- lib/theme_check/checks/asset_size_css_stylesheet_tag.rb
|
125
131
|
- lib/theme_check/checks/asset_size_javascript.rb
|
@@ -209,6 +215,7 @@ files:
|
|
209
215
|
- lib/theme_check/tags.rb
|
210
216
|
- lib/theme_check/template.rb
|
211
217
|
- lib/theme_check/theme.rb
|
218
|
+
- lib/theme_check/theme_file.rb
|
212
219
|
- lib/theme_check/version.rb
|
213
220
|
- lib/theme_check/visitor.rb
|
214
221
|
- packaging/homebrew/theme_check.base.rb
|