theme-check 1.5.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/theme-check.yml +12 -4
  3. data/CHANGELOG.md +34 -0
  4. data/CONTRIBUTING.md +58 -0
  5. data/Gemfile +3 -0
  6. data/docs/flamegraph.svg +18488 -0
  7. data/lib/theme_check/analyzer.rb +5 -0
  8. data/lib/theme_check/asset_file.rb +13 -2
  9. data/lib/theme_check/check.rb +1 -1
  10. data/lib/theme_check/checks/asset_size_css.rb +15 -0
  11. data/lib/theme_check/checks/asset_size_css_stylesheet_tag.rb +18 -1
  12. data/lib/theme_check/checks/convert_include_to_render.rb +2 -1
  13. data/lib/theme_check/checks/liquid_tag.rb +1 -1
  14. data/lib/theme_check/checks/missing_required_template_files.rb +21 -7
  15. data/lib/theme_check/checks/pagination_size.rb +30 -10
  16. data/lib/theme_check/checks/required_directories.rb +3 -1
  17. data/lib/theme_check/checks/space_inside_braces.rb +47 -24
  18. data/lib/theme_check/checks/translation_key_exists.rb +3 -1
  19. data/lib/theme_check/checks/unused_snippet.rb +3 -1
  20. data/lib/theme_check/checks.rb +2 -0
  21. data/lib/theme_check/cli.rb +32 -6
  22. data/lib/theme_check/corrector.rb +23 -10
  23. data/lib/theme_check/file_system_storage.rb +13 -2
  24. data/lib/theme_check/html_node.rb +4 -4
  25. data/lib/theme_check/html_visitor.rb +20 -8
  26. data/lib/theme_check/in_memory_storage.rb +8 -0
  27. data/lib/theme_check/json_file.rb +9 -4
  28. data/lib/theme_check/json_printer.rb +5 -1
  29. data/lib/theme_check/node.rb +118 -11
  30. data/lib/theme_check/offense.rb +26 -0
  31. data/lib/theme_check/position.rb +27 -16
  32. data/lib/theme_check/position_helper.rb +13 -15
  33. data/lib/theme_check/printer.rb +9 -5
  34. data/lib/theme_check/regex_helpers.rb +1 -15
  35. data/lib/theme_check/remote_asset_file.rb +4 -0
  36. data/lib/theme_check/template.rb +5 -19
  37. data/lib/theme_check/template_rewriter.rb +57 -0
  38. data/lib/theme_check/theme_file.rb +18 -1
  39. data/lib/theme_check/version.rb +1 -1
  40. data/lib/theme_check.rb +1 -0
  41. data/theme-check.gemspec +1 -0
  42. metadata +18 -2
@@ -7,31 +7,29 @@ module ThemeCheck
7
7
  return 0 unless content.is_a?(String) && !content.empty?
8
8
  return 0 unless row.is_a?(Integer) && col.is_a?(Integer)
9
9
  i = 0
10
- result = 0
11
- safe_row = bounded(0, row, content.lines.size - 1)
12
- lines = content.lines
13
- line_size = lines[i].size
14
- while i < safe_row
15
- result += line_size
16
- i += 1
17
- line_size = lines[i].size
18
- end
19
- result += bounded(0, col, line_size - 1)
20
- result
10
+ safe_row = bounded(0, row, content.count("\n"))
11
+ scanner = StringScanner.new(content)
12
+ scanner.scan_until(/\n/) while i < safe_row && (i += 1)
13
+ result = scanner.charpos || 0
14
+ scanner.scan_until(/\n|\z/)
15
+ bounded(result, result + col, scanner.pre_match.size)
21
16
  end
22
17
 
23
18
  def from_index_to_row_column(content, index)
24
19
  return [0, 0] unless content.is_a?(String) && !content.empty?
25
20
  return [0, 0] unless index.is_a?(Integer)
26
21
  safe_index = bounded(0, index, content.size - 1)
27
- lines = content[0..safe_index].lines
28
- row = lines.size - 1
29
- col = lines.last.size - 1
22
+ content_up_to_index = content[0...safe_index]
23
+ row = content_up_to_index.count("\n")
24
+ col = 0
25
+ col += 1 while (safe_index -= 1) && safe_index >= 0 && content[safe_index] != "\n"
30
26
  [row, col]
31
27
  end
32
28
 
33
29
  def bounded(a, x, b)
34
- [a, [x, b].min].max
30
+ return a if x < a
31
+ return b if x > b
32
+ x
35
33
  end
36
34
  end
37
35
  end
@@ -2,14 +2,18 @@
2
2
 
3
3
  module ThemeCheck
4
4
  class Printer
5
+ def initialize(out_stream = STDOUT)
6
+ @out = out_stream
7
+ end
8
+
5
9
  def print(theme, offenses, auto_correct)
6
10
  offenses.each do |offense|
7
11
  print_offense(offense, auto_correct)
8
- puts
12
+ @out.puts
9
13
  end
10
14
 
11
15
  correctable = offenses.select(&:correctable?)
12
- puts "#{theme.all.size} files inspected, #{red(offenses.size.to_s + ' offenses')} detected, \
16
+ @out.puts "#{theme.all.size} files inspected, #{red(offenses.size.to_s + ' offenses')} detected, \
13
17
  #{yellow(correctable.size.to_s + ' offenses')} #{auto_correct ? 'corrected' : 'auto-correctable'}"
14
18
  end
15
19
 
@@ -26,15 +30,15 @@ module ThemeCheck
26
30
  ""
27
31
  end
28
32
 
29
- puts location +
33
+ @out.puts location +
30
34
  colorized_severity(offense.severity) + ": " +
31
35
  yellow(offense.check_name) + ": " +
32
36
  corrected +
33
37
  offense.message + "."
34
38
  if offense.source_excerpt
35
- puts "\t#{offense.source_excerpt}"
39
+ @out.puts "\t#{offense.source_excerpt}"
36
40
  if offense.markup_start_in_excerpt
37
- puts "\t" + (" " * offense.markup_start_in_excerpt) + ("^" * offense.markup.size)
41
+ @out.puts "\t" + (" " * offense.markup_start_in_excerpt) + ("^" * offense.markup.size)
38
42
  end
39
43
  end
40
44
  end
@@ -5,6 +5,7 @@ module ThemeCheck
5
5
  LIQUID_TAG = /#{Liquid::TagStart}.*?#{Liquid::TagEnd}/om
6
6
  LIQUID_VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
7
7
  LIQUID_TAG_OR_VARIABLE = /#{LIQUID_TAG}|#{LIQUID_VARIABLE}/om
8
+ HTML_LIQUID_PLACEHOLDER = /≬[0-9a-z]+#*≬/m
8
9
  START_OR_END_QUOTE = /(^['"])|(['"]$)/
9
10
 
10
11
  def matches(s, re)
@@ -16,20 +17,5 @@ module ThemeCheck
16
17
  end
17
18
  matches
18
19
  end
19
-
20
- def href_to_file_size(href)
21
- # asset_url (+ optional stylesheet_tag) variables
22
- if href =~ /^#{LIQUID_VARIABLE}$/o && href =~ /asset_url/ && href =~ Liquid::QuotedString
23
- asset_id = Regexp.last_match(0).gsub(START_OR_END_QUOTE, "")
24
- asset = @theme.assets.find { |a| a.name.end_with?("/" + asset_id) }
25
- return if asset.nil?
26
- asset.gzipped_size
27
-
28
- # remote URLs
29
- elsif href =~ %r{^(https?:)?//}
30
- asset = RemoteAssetFile.from_src(href)
31
- asset.gzipped_size
32
- end
33
- end
34
20
  end
35
21
  end
@@ -17,6 +17,8 @@ module ThemeCheck
17
17
 
18
18
  def uri(src)
19
19
  URI.parse(src.sub(%r{^//}, "https://"))
20
+ rescue URI::InvalidURIError
21
+ nil
20
22
  end
21
23
  end
22
24
 
@@ -26,6 +28,7 @@ module ThemeCheck
26
28
  end
27
29
 
28
30
  def content
31
+ return if @uri.nil?
29
32
  return @content unless @content.nil?
30
33
 
31
34
  res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: @uri.scheme == 'https') do |http|
@@ -41,6 +44,7 @@ module ThemeCheck
41
44
  end
42
45
 
43
46
  def gzipped_size
47
+ return if @uri.nil?
44
48
  @gzipped_size ||= content.bytesize
45
49
  end
46
50
  end
@@ -3,10 +3,11 @@
3
3
  module ThemeCheck
4
4
  class Template < ThemeFile
5
5
  def write
6
- content = updated_content
6
+ content = rewriter.to_s
7
7
  if source != content
8
- @storage.write(@relative_path, content)
8
+ @storage.write(@relative_path, content.gsub("\n", @eol))
9
9
  @source = content
10
+ @rewriter = nil
10
11
  end
11
12
  end
12
13
 
@@ -26,19 +27,8 @@ module ThemeCheck
26
27
  name.start_with?('snippets')
27
28
  end
28
29
 
29
- def lines
30
- # Retain trailing newline character
31
- @lines ||= source.split("\n", -1)
32
- end
33
-
34
- # Not entirely obvious but lines is mutable, corrections are to be
35
- # applied on @lines.
36
- def updated_content
37
- lines.join("\n")
38
- end
39
-
40
- def excerpt(line)
41
- lines[line - 1].strip
30
+ def rewriter
31
+ @rewriter ||= TemplateRewriter.new(@relative_path, source)
42
32
  end
43
33
 
44
34
  def source_excerpt(line)
@@ -46,10 +36,6 @@ module ThemeCheck
46
36
  original_lines[line - 1].strip
47
37
  end
48
38
 
49
- def full_line(line)
50
- lines[line - 1]
51
- end
52
-
53
39
  def parse
54
40
  @ast ||= self.class.parse(source)
55
41
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parser'
4
+
5
+ module ThemeCheck
6
+ class TemplateRewriter
7
+ def initialize(name, source)
8
+ @buffer = Parser::Source::Buffer.new(name, source: source)
9
+ @rewriter = Parser::Source::TreeRewriter.new(
10
+ @buffer
11
+ )
12
+ end
13
+
14
+ def insert_before(node, content)
15
+ @rewriter.insert_before(
16
+ range(node.start_index, node.end_index),
17
+ content
18
+ )
19
+ end
20
+
21
+ def insert_after(node, content)
22
+ @rewriter.insert_after(
23
+ range(node.start_index, node.end_index),
24
+ content
25
+ )
26
+ end
27
+
28
+ def replace(node, content)
29
+ @rewriter.replace(
30
+ range(node.start_index, node.end_index),
31
+ content
32
+ )
33
+ end
34
+
35
+ def wrap(node, insert_before, insert_after)
36
+ @rewriter.wrap(
37
+ range(node.start_index, node.end_index),
38
+ insert_before,
39
+ insert_after,
40
+ )
41
+ end
42
+
43
+ def to_s
44
+ @rewriter.process
45
+ end
46
+
47
+ private
48
+
49
+ def range(start_index, end_index)
50
+ Parser::Source::Range.new(
51
+ @buffer,
52
+ start_index,
53
+ end_index,
54
+ )
55
+ end
56
+ end
57
+ end
@@ -6,6 +6,8 @@ module ThemeCheck
6
6
  def initialize(relative_path, storage)
7
7
  @relative_path = relative_path
8
8
  @storage = storage
9
+ @source = nil
10
+ @eol = "\n"
9
11
  end
10
12
 
11
13
  def path
@@ -20,8 +22,23 @@ module ThemeCheck
20
22
  relative_path.sub_ext('').to_s
21
23
  end
22
24
 
25
+ # For the corrector to work properly, we should have a
26
+ # simple mental model of the internal representation of eol
27
+ # characters (Windows uses \r\n, Linux uses \n).
28
+ #
29
+ # Parser::Source::Buffer strips the \r from the source file, so if
30
+ # you are autocorrecting the file you might lose that info and
31
+ # cause a git diff. It also makes the node.start_index/end_index
32
+ # calculation break. That's not cool.
33
+ #
34
+ # So in here we track whether the source file has \r\n in it and
35
+ # we'll make sure that the file we write has the same eol as the
36
+ # source file.
23
37
  def source
24
- @source ||= @storage.read(@relative_path)
38
+ return @source if @source
39
+ @source = @storage.read(@relative_path)
40
+ @eol = @source.include?("\r\n") ? "\r\n" : "\n"
41
+ @source = @source.gsub("\r\n", "\n")
25
42
  end
26
43
 
27
44
  def json?
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
- VERSION = "1.5.0"
3
+ VERSION = "1.6.1"
4
4
  end
data/lib/theme_check.rb CHANGED
@@ -34,6 +34,7 @@ require_relative "theme_check/string_helpers"
34
34
  require_relative "theme_check/file_system_storage"
35
35
  require_relative "theme_check/in_memory_storage"
36
36
  require_relative "theme_check/tags"
37
+ require_relative "theme_check/template_rewriter"
37
38
  require_relative "theme_check/template"
38
39
  require_relative "theme_check/theme"
39
40
  require_relative "theme_check/visitor"
data/theme-check.gemspec CHANGED
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
26
26
 
27
27
  spec.add_dependency('liquid', '>= 5.0.1')
28
28
  spec.add_dependency('nokogiri', '>= 1.12')
29
+ spec.add_dependency('parser', '~> 3')
29
30
  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: 1.5.0
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Cournoyer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-07 00:00:00.000000000 Z
11
+ date: 2021-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liquid
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: parser
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
41
55
  description:
42
56
  email:
43
57
  - marcandre.cournoyer@shopify.com
@@ -118,6 +132,7 @@ files:
118
132
  - docs/checks/valid_html_translation.md
119
133
  - docs/checks/valid_json.md
120
134
  - docs/checks/valid_schema.md
135
+ - docs/flamegraph.svg
121
136
  - docs/preview.png
122
137
  - exe/theme-check
123
138
  - exe/theme-check-language-server
@@ -228,6 +243,7 @@ files:
228
243
  - lib/theme_check/string_helpers.rb
229
244
  - lib/theme_check/tags.rb
230
245
  - lib/theme_check/template.rb
246
+ - lib/theme_check/template_rewriter.rb
231
247
  - lib/theme_check/theme.rb
232
248
  - lib/theme_check/theme_file.rb
233
249
  - lib/theme_check/version.rb