theme-check 0.10.1 → 1.2.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 +2 -6
- data/CHANGELOG.md +41 -0
- data/README.md +39 -0
- data/RELEASING.md +34 -2
- data/Rakefile +1 -1
- data/config/default.yml +39 -3
- data/config/nothing.yml +11 -0
- data/config/theme_app_extension.yml +153 -0
- data/data/shopify_liquid/objects.yml +2 -0
- data/docs/checks/asset_size_app_block_css.md +52 -0
- data/docs/checks/asset_size_app_block_javascript.md +57 -0
- data/docs/checks/asset_size_css_stylesheet_tag.md +50 -0
- data/docs/checks/deprecate_bgsizes.md +66 -0
- data/docs/checks/deprecate_lazysizes.md +61 -0
- data/docs/checks/html_parsing_error.md +50 -0
- data/docs/checks/liquid_tag.md +2 -2
- data/docs/checks/template_length.md +12 -2
- data/exe/theme-check-language-server.bat +3 -0
- data/exe/theme-check.bat +3 -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 +24 -2
- 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/checks/asset_size_css.rb +11 -74
- data/lib/theme_check/checks/asset_size_css_stylesheet_tag.rb +24 -0
- data/lib/theme_check/checks/asset_size_javascript.rb +10 -36
- data/lib/theme_check/checks/deprecate_bgsizes.rb +14 -0
- data/lib/theme_check/checks/deprecate_lazysizes.rb +16 -0
- data/lib/theme_check/checks/html_parsing_error.rb +12 -0
- data/lib/theme_check/checks/img_lazy_loading.rb +1 -6
- data/lib/theme_check/checks/liquid_tag.rb +2 -2
- data/lib/theme_check/checks/remote_asset.rb +2 -0
- data/lib/theme_check/checks/space_inside_braces.rb +1 -1
- data/lib/theme_check/checks/template_length.rb +18 -4
- 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/html_check.rb +2 -0
- data/lib/theme_check/html_visitor.rb +3 -1
- 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/language_server/handler.rb +1 -1
- data/lib/theme_check/language_server/server.rb +13 -2
- data/lib/theme_check/liquid_check.rb +0 -12
- data/lib/theme_check/parsing_helpers.rb +3 -1
- data/lib/theme_check/regex_helpers.rb +17 -0
- data/lib/theme_check/tags.rb +62 -8
- 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 +22 -3
@@ -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
|
@@ -52,8 +52,19 @@ module ThemeCheck
|
|
52
52
|
response_body = JSON.dump(response)
|
53
53
|
log(JSON.pretty_generate(response)) if $DEBUG
|
54
54
|
|
55
|
-
|
56
|
-
|
55
|
+
# Because programming is fun,
|
56
|
+
#
|
57
|
+
# Ruby on Windows turns \n into \r\n. Which means that \r\n
|
58
|
+
# gets turned into \r\r\n. Which means that the protocol
|
59
|
+
# breaks on windows unless we turn STDOUT into binary mode and
|
60
|
+
# set the encoding manually (yuk!) or we do this little hack
|
61
|
+
# here and put \n which gets transformed into \r\n on windows
|
62
|
+
# only...
|
63
|
+
#
|
64
|
+
# Hours wasted: 8.
|
65
|
+
eol = Gem.win_platform? ? "\n" : "\r\n"
|
66
|
+
@out.write("Content-Length: #{response_body.bytesize}#{eol}")
|
67
|
+
@out.write(eol)
|
57
68
|
@out.write(response_body)
|
58
69
|
@out.flush
|
59
70
|
end
|
@@ -5,17 +5,5 @@ module ThemeCheck
|
|
5
5
|
class LiquidCheck < Check
|
6
6
|
extend ChecksTracking
|
7
7
|
include ParsingHelpers
|
8
|
-
|
9
|
-
# TODO: remove this once all regex checks are migrate to HtmlCheck# TODO: remove this once all regex checks are migrate to HtmlCheck
|
10
|
-
TAG = /#{Liquid::TagStart}.*?#{Liquid::TagEnd}/om
|
11
|
-
VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
|
12
|
-
START_OR_END_QUOTE = /(^['"])|(['"]$)/
|
13
|
-
QUOTED_LIQUID_ATTRIBUTE = %r{
|
14
|
-
'(?:#{TAG}|#{VARIABLE}|[^'])*'| # any combination of tag/variable or non straight quote inside straight quotes
|
15
|
-
"(?:#{TAG}|#{VARIABLE}|[^"])*" # any combination of tag/variable or non double quotes inside double quotes
|
16
|
-
}omix
|
17
|
-
ATTR = /[a-z0-9-]+/i
|
18
|
-
HTML_ATTRIBUTE = /#{ATTR}(?:=#{QUOTED_LIQUID_ATTRIBUTE})?/omix
|
19
|
-
HTML_ATTRIBUTES = /(?:#{HTML_ATTRIBUTE}|\s)*/omix
|
20
8
|
end
|
21
9
|
end
|
@@ -7,8 +7,10 @@ module ThemeCheck
|
|
7
7
|
|
8
8
|
while scanner.scan(/.*?("|')/)
|
9
9
|
yield scanner.matched[0..-2]
|
10
|
+
quote = scanner.matched[-1] == "'" ? "'" : "\""
|
10
11
|
# Skip to the end of the string
|
11
|
-
|
12
|
+
# Check for empty string first, since follow regexp uses lookahead
|
13
|
+
scanner.skip(/#{quote}/) || scanner.skip_until(/[^\\]#{quote}/)
|
12
14
|
end
|
13
15
|
|
14
16
|
yield scanner.rest if scanner.rest?
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ThemeCheck
|
4
4
|
module RegexHelpers
|
5
|
+
VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
|
6
|
+
START_OR_END_QUOTE = /(^['"])|(['"]$)/
|
5
7
|
def matches(s, re)
|
6
8
|
start_at = 0
|
7
9
|
matches = []
|
@@ -11,5 +13,20 @@ module ThemeCheck
|
|
11
13
|
end
|
12
14
|
matches
|
13
15
|
end
|
16
|
+
|
17
|
+
def href_to_file_size(href)
|
18
|
+
# asset_url (+ optional stylesheet_tag) variables
|
19
|
+
if href =~ /^#{VARIABLE}$/o && href =~ /asset_url/ && href =~ Liquid::QuotedString
|
20
|
+
asset_id = Regexp.last_match(0).gsub(START_OR_END_QUOTE, "")
|
21
|
+
asset = @theme.assets.find { |a| a.name.end_with?("/" + asset_id) }
|
22
|
+
return if asset.nil?
|
23
|
+
asset.gzipped_size
|
24
|
+
|
25
|
+
# remote URLs
|
26
|
+
elsif href =~ %r{^(https?:)?//}
|
27
|
+
asset = RemoteAssetFile.from_src(href)
|
28
|
+
asset.gzipped_size
|
29
|
+
end
|
30
|
+
end
|
14
31
|
end
|
15
32
|
end
|
data/lib/theme_check/tags.rb
CHANGED
@@ -125,6 +125,42 @@ module ThemeCheck
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
+
class Render < Liquid::Tag
|
129
|
+
SYNTAX = /((?:#{Liquid::QuotedString}|#{Liquid::VariableSegment})+)(\s+(with|#{Liquid::Render::FOR})\s+(#{Liquid::QuotedFragment}+))?(\s+(?:as)\s+(#{Liquid::VariableSegment}+))?/o
|
130
|
+
|
131
|
+
disable_tags "include"
|
132
|
+
|
133
|
+
attr_reader :template_name_expr, :attributes
|
134
|
+
|
135
|
+
def initialize(tag_name, markup, options)
|
136
|
+
super
|
137
|
+
|
138
|
+
raise SyntaxError, options[:locale].t("errors.syntax.render") unless markup =~ SYNTAX
|
139
|
+
|
140
|
+
template_name = Regexp.last_match(1)
|
141
|
+
with_or_for = Regexp.last_match(3)
|
142
|
+
variable_name = Regexp.last_match(4)
|
143
|
+
|
144
|
+
@alias_name = Regexp.last_match(6)
|
145
|
+
@variable_name_expr = variable_name ? parse_expression(variable_name) : nil
|
146
|
+
@template_name_expr = parse_expression(template_name)
|
147
|
+
@for = (with_or_for == Liquid::Render::FOR)
|
148
|
+
|
149
|
+
@attributes = {}
|
150
|
+
markup.scan(Liquid::TagAttributes) do |key, value|
|
151
|
+
@attributes[key] = parse_expression(value)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
156
|
+
def children
|
157
|
+
[
|
158
|
+
@node.template_name_expr,
|
159
|
+
] + @node.attributes.values
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
128
164
|
class Style < Liquid::Block; end
|
129
165
|
|
130
166
|
class Schema < Liquid::Raw; end
|
@@ -133,13 +169,31 @@ module ThemeCheck
|
|
133
169
|
|
134
170
|
class Stylesheet < Liquid::Raw; end
|
135
171
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
144
198
|
end
|
145
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:
|
4
|
+
version: 1.2.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-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -44,6 +44,8 @@ email:
|
|
44
44
|
executables:
|
45
45
|
- theme-check
|
46
46
|
- theme-check-language-server
|
47
|
+
- theme-check-language-server.bat
|
48
|
+
- theme-check.bat
|
47
49
|
extensions: []
|
48
50
|
extra_rdoc_files: []
|
49
51
|
files:
|
@@ -62,6 +64,8 @@ files:
|
|
62
64
|
- Rakefile
|
63
65
|
- bin/liquid-server
|
64
66
|
- config/default.yml
|
67
|
+
- config/nothing.yml
|
68
|
+
- config/theme_app_extension.yml
|
65
69
|
- data/shopify_liquid/deprecated_filters.yml
|
66
70
|
- data/shopify_liquid/filters.yml
|
67
71
|
- data/shopify_liquid/objects.yml
|
@@ -74,13 +78,19 @@ files:
|
|
74
78
|
- docs/api/json_check.md
|
75
79
|
- docs/api/liquid_check.md
|
76
80
|
- docs/checks/TEMPLATE.md.erb
|
81
|
+
- docs/checks/asset_size_app_block_css.md
|
82
|
+
- docs/checks/asset_size_app_block_javascript.md
|
77
83
|
- docs/checks/asset_size_css.md
|
84
|
+
- docs/checks/asset_size_css_stylesheet_tag.md
|
78
85
|
- docs/checks/asset_size_javascript.md
|
79
86
|
- docs/checks/asset_url_filters.md
|
80
87
|
- docs/checks/content_for_header_modification.md
|
81
88
|
- docs/checks/convert_include_to_render.md
|
82
89
|
- docs/checks/default_locale.md
|
90
|
+
- docs/checks/deprecate_bgsizes.md
|
91
|
+
- docs/checks/deprecate_lazysizes.md
|
83
92
|
- docs/checks/deprecated_filter.md
|
93
|
+
- docs/checks/html_parsing_error.md
|
84
94
|
- docs/checks/img_lazy_loading.md
|
85
95
|
- docs/checks/img_width_and_height.md
|
86
96
|
- docs/checks/liquid_tag.md
|
@@ -109,6 +119,8 @@ files:
|
|
109
119
|
- docs/preview.png
|
110
120
|
- exe/theme-check
|
111
121
|
- exe/theme-check-language-server
|
122
|
+
- exe/theme-check-language-server.bat
|
123
|
+
- exe/theme-check.bat
|
112
124
|
- lib/theme_check.rb
|
113
125
|
- lib/theme_check/analyzer.rb
|
114
126
|
- lib/theme_check/asset_file.rb
|
@@ -116,13 +128,19 @@ files:
|
|
116
128
|
- lib/theme_check/check.rb
|
117
129
|
- lib/theme_check/checks.rb
|
118
130
|
- lib/theme_check/checks/TEMPLATE.rb.erb
|
131
|
+
- lib/theme_check/checks/asset_size_app_block_css.rb
|
132
|
+
- lib/theme_check/checks/asset_size_app_block_javascript.rb
|
119
133
|
- lib/theme_check/checks/asset_size_css.rb
|
134
|
+
- lib/theme_check/checks/asset_size_css_stylesheet_tag.rb
|
120
135
|
- lib/theme_check/checks/asset_size_javascript.rb
|
121
136
|
- lib/theme_check/checks/asset_url_filters.rb
|
122
137
|
- lib/theme_check/checks/content_for_header_modification.rb
|
123
138
|
- lib/theme_check/checks/convert_include_to_render.rb
|
124
139
|
- lib/theme_check/checks/default_locale.rb
|
140
|
+
- lib/theme_check/checks/deprecate_bgsizes.rb
|
141
|
+
- lib/theme_check/checks/deprecate_lazysizes.rb
|
125
142
|
- lib/theme_check/checks/deprecated_filter.rb
|
143
|
+
- lib/theme_check/checks/html_parsing_error.rb
|
126
144
|
- lib/theme_check/checks/img_lazy_loading.rb
|
127
145
|
- lib/theme_check/checks/img_width_and_height.rb
|
128
146
|
- lib/theme_check/checks/liquid_tag.rb
|
@@ -201,6 +219,7 @@ files:
|
|
201
219
|
- lib/theme_check/tags.rb
|
202
220
|
- lib/theme_check/template.rb
|
203
221
|
- lib/theme_check/theme.rb
|
222
|
+
- lib/theme_check/theme_file.rb
|
204
223
|
- lib/theme_check/version.rb
|
205
224
|
- lib/theme_check/visitor.rb
|
206
225
|
- packaging/homebrew/theme_check.base.rb
|
@@ -225,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
225
244
|
- !ruby/object:Gem::Version
|
226
245
|
version: '0'
|
227
246
|
requirements: []
|
228
|
-
rubygems_version: 3.2.
|
247
|
+
rubygems_version: 3.2.20
|
229
248
|
signing_key:
|
230
249
|
specification_version: 4
|
231
250
|
summary: A Shopify Theme Linter
|