theme-check 0.7.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/theme-check.yml +1 -0
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +31 -0
  5. data/data/shopify_translation_keys.yml +1 -0
  6. data/dev.yml +1 -1
  7. data/docs/checks/space_inside_braces.md +6 -0
  8. data/exe/theme-check +1 -1
  9. data/lib/theme_check.rb +4 -0
  10. data/lib/theme_check/analyzer.rb +19 -9
  11. data/lib/theme_check/check.rb +5 -1
  12. data/lib/theme_check/checks.rb +2 -8
  13. data/lib/theme_check/checks/missing_enable_comment.rb +4 -4
  14. data/lib/theme_check/checks/remote_asset.rb +2 -1
  15. data/lib/theme_check/checks/space_inside_braces.rb +8 -2
  16. data/lib/theme_check/cli.rb +99 -64
  17. data/lib/theme_check/config.rb +6 -2
  18. data/lib/theme_check/disabled_check.rb +39 -0
  19. data/lib/theme_check/disabled_checks.rb +20 -32
  20. data/lib/theme_check/exceptions.rb +32 -0
  21. data/lib/theme_check/json_file.rb +5 -1
  22. data/lib/theme_check/language_server.rb +0 -1
  23. data/lib/theme_check/language_server/completion_engine.rb +1 -1
  24. data/lib/theme_check/language_server/constants.rb +6 -2
  25. data/lib/theme_check/language_server/document_link_engine.rb +2 -2
  26. data/lib/theme_check/language_server/handler.rb +32 -24
  27. data/lib/theme_check/node.rb +12 -0
  28. data/lib/theme_check/offense.rb +16 -48
  29. data/lib/theme_check/packager.rb +1 -1
  30. data/lib/theme_check/parsing_helpers.rb +1 -1
  31. data/lib/theme_check/position.rb +77 -0
  32. data/lib/theme_check/position_helper.rb +37 -0
  33. data/lib/theme_check/remote_asset_file.rb +3 -0
  34. data/lib/theme_check/version.rb +1 -1
  35. data/lib/theme_check/visitor.rb +9 -10
  36. data/packaging/homebrew/theme_check.base.rb +1 -1
  37. data/theme-check.gemspec +2 -0
  38. metadata +7 -4
  39. data/lib/theme_check/language_server/position_helper.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c75935127b3ec2257d0e857c46b501a0c5189da619776202a2180234606c84ca
4
- data.tar.gz: 3a54ffed715331b78f6ae3949b76466068539cd9fea4275e64f9f7a413ffd22b
3
+ metadata.gz: b841f4d2f809e07dec57140fd76f373e216f721903baa7ac1dce747b5ce77c82
4
+ data.tar.gz: b513333c296966cedb4d00345cb7ab1a3d4dcbb305b92b3eb96edc598867bba5
5
5
  SHA512:
6
- metadata.gz: 2720180bf217fd64061f6ed4edc5ba01616fbb6ec6b30254638c992924719d179af0f2d72c4a1a1330c7b1e6a0ef9856aec4567aca73161a0cd2b59033fffcf4
7
- data.tar.gz: c1d79dad05a55c374d4a2e008484d3c527f4483174e66ffb3c30ce96f65e8a2f03c6295de33130295b70bc63f3d3aed4dce2c7eac14de0347aa73fca599b062e
6
+ metadata.gz: 1d75dc49ae97db15d4622d6a59b7a12a0eab0de6a6bcf19e6e8bca3b7f6eb6f94c40c11cfcdb3ac911bbc4d93e6d08cfbb83e85de763d00cb60b6fab10bfc7bb
7
+ data.tar.gz: eb7c5fdb74551461de7fd643071734dc8b2442db4a5a80492b982bc392c67a100614e4a9fa3b4580e28c5f1b8321564adc8f9580618f3bcdbc36dfd4e4ab13e3
@@ -15,6 +15,7 @@ jobs:
15
15
  version:
16
16
  - 3.0.0
17
17
  - 2.7.1
18
+ - 2.6.6
18
19
 
19
20
  name: Ruby ${{ matrix.version }}
20
21
 
data/.rubocop.yml CHANGED
@@ -7,7 +7,7 @@ require:
7
7
  - rubocop-rake
8
8
 
9
9
  AllCops:
10
- TargetRubyVersion: 2.7
10
+ TargetRubyVersion: 2.6
11
11
  Exclude:
12
12
  - 'vendor/bundle/**/*'
13
13
  - 'packaging/builds/**/*'
data/CHANGELOG.md CHANGED
@@ -1,4 +1,35 @@
1
1
 
2
+ 0.8.1 / 2021-04-22
3
+ ==================
4
+
5
+ * Add consistent spacing around the pipe character (`|`) in variable expressions to the `SpaceInsideBrace` check ([#73](https://github.com/shopify/theme-check/issues/73))
6
+ * Add ReCaptcha system translation ([#265](https://github.com/shopify/theme-check/issues/265))
7
+ * Fix document links in `{% liquid %}` tags ([#263](https://github.com/shopify/theme-check/issues/263))
8
+ * Fix theme-check-disable for checks based on regular expressions ([#242](https://github.com/shopify/theme-check/issues/242))
9
+ * Fix VS Code crash on new window ([#264](https://github.com/shopify/theme-check/issues/264))
10
+ * Rescue errors thrown by remote_asset_file
11
+
12
+ 0.8.0 / 2021-04-13
13
+ ==================
14
+
15
+ * Set minimal Ruby version to 2.6
16
+
17
+ 0.7.3 / 2021-04-13
18
+ ==================
19
+
20
+ * Refactor CLI option parsing
21
+
22
+ 0.7.2 / 2021-04-12
23
+ ==================
24
+
25
+ * Fixup bug in RemoteAsset causing Language Server to break
26
+
27
+ 0.7.1 / 2021-04-12
28
+ ==================
29
+
30
+ * Fix High CPU Bug in RemoteAsset check
31
+ * Fix document link for render tags that trim whitespace.
32
+
2
33
  0.7.0 / 2021-04-08
3
34
  ==================
4
35
 
@@ -848,3 +848,4 @@
848
848
  - shopify.store_availability.pick_up_time.two_to_four_hours
849
849
  - shopify.store_availability.pick_up_time.immediately
850
850
  - shopify.store_availability.pick_up_time.next_day
851
+ - shopify.online_store.spam_detection.disclaimer_html
data/dev.yml CHANGED
@@ -3,7 +3,7 @@ name: theme-check
3
3
  type: ruby
4
4
 
5
5
  up:
6
- - ruby: 2.7.1
6
+ - ruby: 2.6.6
7
7
  - bundler
8
8
 
9
9
  commands:
@@ -17,6 +17,10 @@ This check is aimed at eliminating ugly Liquid:
17
17
  <!-- After commas and semicolons -->
18
18
  {% form 'type', object, key:value %}
19
19
  {% endform %}
20
+
21
+ <!-- Arround filter pipelines -->
22
+ {{ url | asset_url | img_tag }}
23
+ {% assign my_upcase_string = "Hello world"| upcase %}
20
24
  ```
21
25
 
22
26
  :+1: Examples of **correct** code for this check:
@@ -33,6 +37,8 @@ This check is aimed at eliminating ugly Liquid:
33
37
  media_size: section.settings.product_recommendations_image_ratio,
34
38
  center_align_text: section.settings.center_align_text
35
39
  %}
40
+ {{ url | asset_url | img_tag }}
41
+ {% assign my_upcase_string = "Hello world" | upcase %}
36
42
  ```
37
43
 
38
44
  ## Check Options
data/exe/theme-check CHANGED
@@ -3,4 +3,4 @@
3
3
 
4
4
  require "theme_check"
5
5
 
6
- ThemeCheck::Cli.new.run!(ARGV)
6
+ ThemeCheck::Cli.parse_and_run(ARGV)
data/lib/theme_check.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  require "liquid"
3
3
 
4
+ require_relative "theme_check/exceptions"
4
5
  require_relative "theme_check/analyzer"
5
6
  require_relative "theme_check/check"
6
7
  require_relative "theme_check/checks_tracking"
7
8
  require_relative "theme_check/cli"
9
+ require_relative "theme_check/disabled_check"
8
10
  require_relative "theme_check/disabled_checks"
9
11
  require_relative "theme_check/liquid_check"
10
12
  require_relative "theme_check/locale_diff"
@@ -14,6 +16,8 @@ require_relative "theme_check/regex_helpers"
14
16
  require_relative "theme_check/json_check"
15
17
  require_relative "theme_check/json_file"
16
18
  require_relative "theme_check/json_helpers"
19
+ require_relative "theme_check/position_helper"
20
+ require_relative "theme_check/position"
17
21
  require_relative "theme_check/language_server"
18
22
  require_relative "theme_check/checks"
19
23
  require_relative "theme_check/config"
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
3
  class Analyzer
4
- attr_reader :offenses
5
-
6
4
  def initialize(theme, checks = Check.all.map(&:new), auto_correct = false)
7
5
  @theme = theme
8
- @offenses = []
9
6
  @auto_correct = auto_correct
10
7
 
11
8
  @liquid_checks = Checks.new
@@ -13,7 +10,6 @@ module ThemeCheck
13
10
 
14
11
  checks.each do |check|
15
12
  check.theme = @theme
16
- check.offenses = @offenses
17
13
 
18
14
  case check
19
15
  when LiquidCheck
@@ -26,26 +22,40 @@ module ThemeCheck
26
22
  @visitor = Visitor.new(@liquid_checks)
27
23
  end
28
24
 
25
+ def offenses
26
+ @liquid_checks.flat_map(&:offenses) + @json_checks.flat_map(&:offenses)
27
+ end
28
+
29
+ def offenses_clear!
30
+ @liquid_checks.each do |check|
31
+ check.offenses.clear
32
+ end
33
+
34
+ @json_checks.each do |check|
35
+ check.offenses.clear
36
+ end
37
+ end
38
+
29
39
  def analyze_theme
30
- @offenses.clear
40
+ offenses_clear!
31
41
  @theme.liquid.each { |template| @visitor.visit_template(template) }
32
42
  @theme.json.each { |json_file| @json_checks.call(:on_file, json_file) }
33
43
  @liquid_checks.call(:on_end)
34
44
  @json_checks.call(:on_end)
35
- @offenses
45
+ offenses
36
46
  end
37
47
 
38
48
  def uncorrectable_offenses
39
49
  unless @auto_correct
40
- return @offenses
50
+ return offenses
41
51
  end
42
52
 
43
- @offenses.select { |offense| !offense.correctable? }
53
+ offenses.select { |offense| !offense.correctable? }
44
54
  end
45
55
 
46
56
  def correct_offenses
47
57
  if @auto_correct
48
- @offenses.each(&:correct)
58
+ offenses.each(&:correct)
49
59
  @theme.liquid.each(&:write)
50
60
  end
51
61
  end
@@ -6,8 +6,8 @@ module ThemeCheck
6
6
  include JsonHelpers
7
7
 
8
8
  attr_accessor :theme
9
- attr_accessor :offenses
10
9
  attr_accessor :options
10
+ attr_writer :offenses
11
11
 
12
12
  SEVERITIES = [
13
13
  :error,
@@ -69,6 +69,10 @@ module ThemeCheck
69
69
  end
70
70
  end
71
71
 
72
+ def offenses
73
+ @offenses ||= []
74
+ end
75
+
72
76
  def severity
73
77
  self.class.severity
74
78
  end
@@ -9,14 +9,8 @@ module ThemeCheck
9
9
  end
10
10
  end
11
11
 
12
- def always_enabled
13
- self.class.new(reject(&:can_disable?))
14
- end
15
-
16
- def except_for(disabled_checks)
17
- still_enabled = reject { |check| disabled_checks.all.include?(check.code_name) }
18
-
19
- self.class.new((always_enabled + still_enabled).uniq)
12
+ def disableable
13
+ self.class.new(select(&:can_disable?))
20
14
  end
21
15
  end
22
16
  end
@@ -17,13 +17,13 @@ module ThemeCheck
17
17
  end
18
18
 
19
19
  def after_document(node)
20
- return if @disabled_checks.full_document_disabled?
21
- return unless @disabled_checks.any?
20
+ checks_missing_end_index = @disabled_checks.checks_missing_end_index
21
+ return if checks_missing_end_index.empty?
22
22
 
23
- message = if @disabled_checks.all_disabled?
23
+ message = if checks_missing_end_index.any? { |name| name == :all }
24
24
  "All checks were"
25
25
  else
26
- @disabled_checks.all.join(', ') + " " + (@disabled_checks.all.size == 1 ? "was" : "were")
26
+ checks_missing_end_index.join(', ') + " " + (checks_missing_end_index.size == 1 ? "was" : "were")
27
27
  end
28
28
 
29
29
  add_offense("#{message} disabled but not re-enabled with theme-check-enable", node: node)
@@ -24,7 +24,7 @@ module ThemeCheck
24
24
  'shopify_asset_url',
25
25
  ]
26
26
 
27
- RESOURCE_TAG = /<(?<tag_name>img|script|link|source)#{HTML_ATTRIBUTES}>/oim
27
+ RESOURCE_TAG = %r{<(?<tag_name>img|script|link|source)#{HTML_ATTRIBUTES}/?>}oim
28
28
  RESOURCE_URL = /\s(?:src|href)=(?<resource_url>#{QUOTED_LIQUID_ATTRIBUTE})/oim
29
29
  ASSET_URL_FILTER = /[\|\s]*(#{ASSET_URL_FILTERS.join('|')})/omi
30
30
  PROTOCOL = %r{(https?:)?//}
@@ -76,6 +76,7 @@ module ThemeCheck
76
76
  next if url_hosted_by_shopify?(resource_url)
77
77
  next if resource_url =~ ABSOLUTE_PATH
78
78
  next if resource_url =~ RELATIVE_PATH
79
+ next if resource_url.empty?
79
80
 
80
81
  start = match.begin(0) + resource_match.begin(:resource_url)
81
82
  add_offense(
@@ -15,12 +15,18 @@ module ThemeCheck
15
15
  return if :assign == node.type_name
16
16
 
17
17
  outside_of_strings(node.markup) do |chunk|
18
- chunk.scan(/([,:]) +/) do |_match|
18
+ chunk.scan(/([,:|]) +/) do |_match|
19
19
  add_offense("Too many spaces after '#{Regexp.last_match(1)}'", node: node, markup: Regexp.last_match(0))
20
20
  end
21
- chunk.scan(/([,:])\S/) do |_match|
21
+ chunk.scan(/([,:|])\S/) do |_match|
22
22
  add_offense("Space missing after '#{Regexp.last_match(1)}'", node: node, markup: Regexp.last_match(0))
23
23
  end
24
+ chunk.scan(/ ([|])+/) do |_match|
25
+ add_offense("Too many spaces before '#{Regexp.last_match(1)}'", node: node, markup: Regexp.last_match(0))
26
+ end
27
+ chunk.scan(/\A(?<pipe>\|)|\S(?<pipe>\|)/) do |_match|
28
+ add_offense("Space missing before '#{Regexp.last_match(:pipe)}'", node: node, markup: Regexp.last_match(:pipe))
29
+ end
24
30
  end
25
31
  end
26
32
 
@@ -1,85 +1,104 @@
1
1
  # frozen_string_literal: true
2
+ require "optparse"
3
+
2
4
  module ThemeCheck
3
5
  class Cli
4
6
  class Abort < StandardError; end
5
7
 
6
- USAGE = <<~END
7
- Usage: theme-check [options] /path/to/your/theme
8
-
9
- Basic Options:
10
- -C, --config <path> Use the config provided, overriding .theme-check.yml if present
11
- -c, --category <category> Only run this category of checks
12
- -x, --exclude-category <category> Exclude this category of checks
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
18
- -h, --help Show this. Hi!
19
- -l, --list List enabled checks
20
- -v, --version Print Theme Check version
21
-
22
- Description:
23
- Theme Check helps you follow Shopify Themes & Liquid best practices by analyzing the
24
- Liquid & JSON inside your theme.
25
-
26
- You can configure checks in the .theme-check.yml file of your theme root directory.
27
- END
8
+ attr_accessor :path
28
9
 
29
- def run(argv)
10
+ def initialize
30
11
  @path = "."
12
+ @command = :check
13
+ @only_categories = []
14
+ @exclude_categories = []
15
+ @auto_correct = false
16
+ @config_path = nil
17
+ end
31
18
 
32
- command = :check
33
- only_categories = []
34
- exclude_categories = []
35
- auto_correct = false
36
- config_path = nil
37
-
38
- args = argv.dup
39
- while (arg = args.shift)
40
- case arg
41
- when "--help", "-h"
42
- raise Abort, USAGE
43
- when "--version", "-v"
44
- command = :version
45
- when "--config", "-C"
46
- config_path = Pathname.new(args.shift)
47
- when "--category", "-c"
48
- only_categories << args.shift.to_sym
49
- when "--exclude-category", "-x"
50
- exclude_categories << args.shift.to_sym
51
- when "--list", "-l"
52
- command = :list
53
- when "--auto-correct", "-a"
54
- auto_correct = true
55
- when "--init"
56
- command = :init
57
- when "--print"
58
- command = :print
59
- else
60
- @path = arg
61
- end
62
- end
19
+ def option_parser(parser = OptionParser.new, help: true)
20
+ return @option_parser if defined?(@option_parser)
21
+ @option_parser = parser
22
+ @option_parser.banner = "Usage: theme-check [options] [/path/to/your/theme]"
23
+
24
+ @option_parser.separator("")
25
+ @option_parser.separator("Basic Options:")
26
+ @option_parser.on(
27
+ "-C", "--config PATH",
28
+ "Use the config provided, overriding .theme-check.yml if present"
29
+ ) { |path| @config_path = path }
30
+ @option_parser.on(
31
+ "-c", "--category CATEGORY",
32
+ "Only run this category of checks"
33
+ ) { |category| @only_categories << category.to_sym }
34
+ @option_parser.on(
35
+ "-x", "--exclude-category CATEGORY",
36
+ "Exclude this category of checks"
37
+ ) { |category| @exclude_categories << category.to_sym }
38
+ @option_parser.on(
39
+ "-a", "--auto-correct",
40
+ "Automatically fix offenses"
41
+ ) { @auto_correct = true }
42
+
43
+ @option_parser.separator("")
44
+ @option_parser.separator("Miscellaneous:")
45
+ @option_parser.on(
46
+ "--init",
47
+ "Generate a .theme-check.yml file"
48
+ ) { @command = :init }
49
+ @option_parser.on(
50
+ "--print",
51
+ "Output active config to STDOUT"
52
+ ) { @command = :print }
53
+ @option_parser.on(
54
+ "-h", "--help",
55
+ "Show this. Hi!"
56
+ ) { @command = :help } if help
57
+ @option_parser.on(
58
+ "-l", "--list",
59
+ "List enabled checks"
60
+ ) { @command = :list }
61
+ @option_parser.on(
62
+ "-v", "--version",
63
+ "Print Theme Check version"
64
+ ) { @command = :version }
65
+
66
+ @option_parser.separator("")
67
+ @option_parser.separator(<<~EOS)
68
+ Description:
69
+ Theme Check helps you follow Shopify Themes & Liquid best practices by analyzing the
70
+ Liquid & JSON inside your theme.
71
+
72
+ You can configure checks in the .theme-check.yml file of your theme root directory.
73
+ EOS
74
+
75
+ @option_parser
76
+ end
77
+
78
+ def parse(argv)
79
+ @path = option_parser.parse(argv).first || "."
80
+ end
63
81
 
64
- unless [:version, :init].include?(command)
65
- @config = if config_path
82
+ def run!
83
+ unless [:version, :init, :help].include?(@command)
84
+ @config = if @config_path
66
85
  ThemeCheck::Config.new(
67
86
  root: @path,
68
- configuration: ThemeCheck::Config.load_file(config_path)
87
+ configuration: ThemeCheck::Config.load_file(@config_path)
69
88
  )
70
89
  else
71
90
  ThemeCheck::Config.from_path(@path)
72
91
  end
73
- @config.only_categories = only_categories
74
- @config.exclude_categories = exclude_categories
75
- @config.auto_correct = auto_correct
92
+ @config.only_categories = @only_categories
93
+ @config.exclude_categories = @exclude_categories
94
+ @config.auto_correct = @auto_correct
76
95
  end
77
96
 
78
- send(command)
97
+ send(@command)
79
98
  end
80
99
 
81
- def run!(argv)
82
- run(argv)
100
+ def run
101
+ run!
83
102
  rescue Abort => e
84
103
  if e.message.empty?
85
104
  exit(1)
@@ -88,6 +107,18 @@ module ThemeCheck
88
107
  end
89
108
  end
90
109
 
110
+ def self.parse_and_run!(argv)
111
+ cli = new
112
+ cli.parse(argv)
113
+ cli.run!
114
+ end
115
+
116
+ def self.parse_and_run(argv)
117
+ cli = new
118
+ cli.parse(argv)
119
+ cli.run
120
+ end
121
+
91
122
  def list
92
123
  puts @config.enabled_checks
93
124
  end
@@ -111,12 +142,16 @@ module ThemeCheck
111
142
  puts YAML.dump(@config.to_h)
112
143
  end
113
144
 
145
+ def help
146
+ puts option_parser.to_s
147
+ end
148
+
114
149
  def check
115
150
  puts "Checking #{@config.root} ..."
116
151
  storage = ThemeCheck::FileSystemStorage.new(@config.root, ignored_patterns: @config.ignored_patterns)
117
152
  theme = ThemeCheck::Theme.new(storage)
118
153
  if theme.all.empty?
119
- raise Abort, "No templates found.\n#{USAGE}"
154
+ raise Abort, "No templates found."
120
155
  end
121
156
  analyzer = ThemeCheck::Analyzer.new(theme, @config.enabled_checks, @config.auto_correct)
122
157
  analyzer.analyze_theme