theme-check 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CONTRIBUTING.md +2 -0
  4. data/README.md +62 -1
  5. data/RELEASING.md +41 -0
  6. data/Rakefile +38 -4
  7. data/config/default.yml +18 -0
  8. data/data/shopify_liquid/deprecated_filters.yml +10 -0
  9. data/data/shopify_liquid/plus_objects.yml +15 -0
  10. data/dev.yml +2 -0
  11. data/lib/theme_check.rb +5 -0
  12. data/lib/theme_check/analyzer.rb +15 -5
  13. data/lib/theme_check/check.rb +11 -0
  14. data/lib/theme_check/checks.rb +10 -0
  15. data/lib/theme_check/checks/deprecated_filter.rb +22 -0
  16. data/lib/theme_check/checks/missing_enable_comment.rb +31 -0
  17. data/lib/theme_check/checks/missing_required_template_files.rb +12 -12
  18. data/lib/theme_check/checks/parser_blocking_javascript.rb +55 -0
  19. data/lib/theme_check/checks/space_inside_braces.rb +19 -7
  20. data/lib/theme_check/checks/template_length.rb +11 -3
  21. data/lib/theme_check/checks/undefined_object.rb +107 -34
  22. data/lib/theme_check/checks/valid_html_translation.rb +2 -2
  23. data/lib/theme_check/cli.rb +18 -4
  24. data/lib/theme_check/config.rb +97 -44
  25. data/lib/theme_check/corrector.rb +31 -0
  26. data/lib/theme_check/disabled_checks.rb +77 -0
  27. data/lib/theme_check/file_system_storage.rb +51 -0
  28. data/lib/theme_check/in_memory_storage.rb +37 -0
  29. data/lib/theme_check/json_file.rb +12 -10
  30. data/lib/theme_check/language_server/handler.rb +38 -13
  31. data/lib/theme_check/language_server/server.rb +2 -2
  32. data/lib/theme_check/liquid_check.rb +2 -2
  33. data/lib/theme_check/node.rb +13 -0
  34. data/lib/theme_check/offense.rb +25 -9
  35. data/lib/theme_check/packager.rb +51 -0
  36. data/lib/theme_check/printer.rb +13 -4
  37. data/lib/theme_check/shopify_liquid.rb +1 -0
  38. data/lib/theme_check/shopify_liquid/deprecated_filter.rb +28 -0
  39. data/lib/theme_check/shopify_liquid/object.rb +6 -0
  40. data/lib/theme_check/storage.rb +25 -0
  41. data/lib/theme_check/template.rb +32 -10
  42. data/lib/theme_check/theme.rb +14 -9
  43. data/lib/theme_check/version.rb +1 -1
  44. data/lib/theme_check/visitor.rb +14 -3
  45. data/packaging/homebrew/theme_check.base.rb +98 -0
  46. metadata +21 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 037f9fd0dba35bf611162475709aef96e0e24c8e63b18eac3ad20b4d65d37255
4
- data.tar.gz: b856c24c2135a805c91c230d1c3a2e0dc178d4f0249faa77bb9a2c263993dbd2
3
+ metadata.gz: a07597b217b1f596cd5e777726950e396934726f3572bc7660129bec2b3b30c8
4
+ data.tar.gz: 80e54966286e332f3308021b9347feb4cdb683497991b9bb451dcf58f138a2df
5
5
  SHA512:
6
- metadata.gz: 1f4cafab1d1a3adf53771682f0faf00b1a327dc3a358a60157a92a3c7625636ce38eb266a271d01891cae0786c0f4c38c35a3ed03cd34d1b9deb7bb86197ba41
7
- data.tar.gz: cc0b7f5d9878c74b19a500d93cb873e0e17610134cb99fee832d1c31e20d6e6d805b494c44897e4d590d7b4c9bec99bb95b10b511d3fa0a13d7e1813035071a1
6
+ metadata.gz: 4c7a1c159dd1b688618c95e1e5fb943a133b606878f57c91b12e5793b07022325c5cfb2e81c49cb45ea484cb3688c6f7c2b75af1b2a50f13da6db2ba0e8b0bb3
7
+ data.tar.gz: 83fe6a74375ecf2b58f14681c99bc672a710cd6bbc70cb5f58ff06332d4123171544f6cf73c6e7f5f750c1a870d17de0ce3bd3e1f9cbfca6e1bdc7fceb767b80
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  Gemfile.lock
12
12
 
13
13
  .rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml
14
+ packaging/builds
data/CONTRIBUTING.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  We love receiving pull requests!
4
4
 
5
+ For your contribution to be accepted you will need to sign the [Shopify Contributor License Agreement (CLA)](https://cla.shopify.com/).
6
+
5
7
  ## Standards
6
8
 
7
9
  * Checks should do one thing, and do it well.
data/README.md CHANGED
@@ -30,13 +30,32 @@ Theme Check currently checks for the following:
30
30
  ✅ Unmatching translation keys in locale files
31
31
  ✅ Using unknown translation keys in `{{ 'missing_key' | t }}`
32
32
  ✅ Using several `{% ... %}` instead of `{% liquid ... %}`
33
- ✅ Undefined [objects](https://shopify.dev/docs/themes/liquid/reference/objects)
33
+ ✅ Undefined [objects](https://shopify.dev/docs/themes/liquid/reference/objects)
34
+ ✅ Deprecated filters
35
+ ✅ Missing `theme-check-enable` comment
34
36
 
35
37
  And many more to come! Suggestions welcome ([create an issue](https://github.com/Shopify/theme-check/issues)).
36
38
 
39
+ ## Requirements
40
+
41
+ - Ruby 2.7+
42
+
37
43
  ## Installation
38
44
 
45
+ Theme Check is available through Homebrew _or_ RubyGems.
46
+
47
+ **Homebrew**
48
+
49
+ You’ll need to run `brew tap` first to add Shopify’s third-party repositories to Homebrew.
50
+
51
+ ```sh
52
+ brew tap shopify/shopify
53
+ brew install theme-check
39
54
  ```
55
+
56
+ **RubyGems**
57
+
58
+ ```sh
40
59
  gem install theme-check
41
60
  ```
42
61
 
@@ -61,11 +80,53 @@ Add a `.theme-check.yml` file at the root of your theme to configure:
61
80
  # be uploaded to Shopify.
62
81
  root: dist
63
82
 
83
+ # It is possible to extend theme-check with custom checks
84
+ require:
85
+ - ./path/to/my_custom_check.rb
86
+
64
87
  # Disable some checks
65
88
  TemplateLength:
66
89
  enabled: false
67
90
  # Or configure options
68
91
  max_length: 300
92
+
93
+ # Enable a custom check
94
+ MyCustomCheck
95
+ enabled: true
69
96
  ```
70
97
 
71
98
  See [config/default.yml](config/default.yml) for available options & defaults.
99
+
100
+ ## Disable checks with comments
101
+
102
+ Use Liquid comments to disable and re-enable all checks for a section of your template:
103
+
104
+ ```liquid
105
+ {% comment %}theme-check-disable{% endcomment %}
106
+ {% assign x = 1 %}
107
+ {% comment %}theme-check-enable{% endcomment %}
108
+ ```
109
+
110
+ Disable a specific check by including it in the comment:
111
+
112
+ ```liquid
113
+ {% comment %}theme-check-disable UnusedAssign{% endcomment %}
114
+ {% assign x = 1 %}
115
+ {% comment %}theme-check-enable UnusedAssign{% endcomment %}
116
+ ```
117
+
118
+ Disable multiple checks by including them as a comma-separated list:
119
+
120
+ ```liquid
121
+ {% comment %}theme-check-disable UnusedAssign,SpaceInsideBraces{% endcomment %}
122
+ {%assign x = 1%}
123
+ {% comment %}theme-check-enable UnusedAssign,SpaceInsideBraces{% endcomment %}
124
+ ```
125
+
126
+ Disable checks for the _entire document_ by placing the comment on the first line:
127
+
128
+ ```liquid
129
+ {% comment %}theme-check-disable SpaceInsideBraces{% endcomment %}
130
+
131
+ {%assign x = 1%}
132
+ ```
data/RELEASING.md ADDED
@@ -0,0 +1,41 @@
1
+ ## Releasing Theme Check
2
+
3
+ 1. Check the Semantic Versioning page for info on how to version the new release: http://semver.org
4
+
5
+ 2. Create a PR to update the version in `lib/theme_check/version.rb`
6
+
7
+ 3. Merge your PR to master
8
+
9
+ 4. On [Shipit](https://shipit.shopify.io/shopify/theme-check/rubygems), deploy your commit.
10
+
11
+ ## Homebrew Release Process
12
+
13
+ 1. Release `theme-check` on RubyGems by following the steps in the previous section.
14
+
15
+ 2. Generate the homebrew formula.
16
+
17
+ ```bash
18
+ rake package
19
+ ```
20
+
21
+ 3. Copy the formula over in the [`homebrew-shopify`](https://github.com/Shopify/homebrew-shopify) repository.
22
+
23
+ ```bash
24
+ VERSION=X.X.X
25
+ cp packaging/builds/$VERSION/theme-check ../homebrew-shopify
26
+ ```
27
+
28
+ 4. Create a branch + a commit on the [`homebrew-shopify`](https://github.com/Shopify/homebrew-shopify) repository.
29
+
30
+ ```bash
31
+ git checkout -b "bump/theme-check-$VERSION"
32
+ git add theme-check.rb
33
+ git commit -m "Bump theme-check version to $VERSION"
34
+ ```
35
+
36
+ 5. Create a pull-request for those changes on the [`homebrew-shopify`](https://github.com/Shopify/homebrew-shopify) repository.
37
+
38
+ ```bash
39
+ # shortcut if you have `hub` installed
40
+ hub compare "master:bump/theme-check-$VERSION"
41
+ ```
data/Rakefile CHANGED
@@ -3,12 +3,46 @@ require "rake/testtask"
3
3
  require "rubocop/rake_task"
4
4
  require "bundler/gem_tasks"
5
5
 
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << "test"
8
- t.libs << "lib"
9
- t.test_files = FileList["test/**/*_test.rb"]
6
+ namespace :tests do
7
+ task all: [:in_memory, :file_system]
8
+
9
+ Rake::TestTask.new(:suite) do |t|
10
+ t.libs << "test"
11
+ t.libs << "lib"
12
+ t.test_files = FileList["test/**/*_test.rb"]
13
+ end
14
+
15
+ desc("Runs the tests with InMemoryStorage")
16
+ task :in_memory do
17
+ ENV["THEME_STORAGE"] = 'InMemoryStorage'
18
+ puts "Running tests with #{ENV['THEME_STORAGE']}"
19
+ Rake::Task['tests:suite'].execute
20
+ end
21
+
22
+ desc("Runs the tests with FileSystemStorage")
23
+ task :file_system do
24
+ ENV["THEME_STORAGE"] = 'FileSystemStorage'
25
+ puts "Running tests with #{ENV['THEME_STORAGE']}"
26
+ Rake::Task['tests:suite'].execute
27
+ end
10
28
  end
11
29
 
30
+ task(test: 'tests:all')
31
+
12
32
  RuboCop::RakeTask.new
13
33
 
14
34
  task default: [:test, :rubocop]
35
+
36
+ namespace :package do
37
+ require 'theme_check/packager'
38
+
39
+ task all: [:homebrew]
40
+
41
+ desc("Builds a Homebrew package of the CLI")
42
+ task :homebrew do
43
+ ThemeCheck::Packager.new.build_homebrew
44
+ end
45
+ end
46
+
47
+ desc("Builds all distribution packages of the CLI")
48
+ task(package: 'package:all')
data/config/default.yml CHANGED
@@ -1,3 +1,10 @@
1
+ root: .
2
+
3
+ require: []
4
+
5
+ ignore:
6
+ - node_modules/*
7
+
1
8
  ConvertIncludeToRender:
2
9
  enabled: true
3
10
 
@@ -22,6 +29,8 @@ SyntaxError:
22
29
  TemplateLength:
23
30
  enabled: true
24
31
  max_length: 200
32
+ # Exclude content of {% schema %} in line count
33
+ exclude_schema: true
25
34
 
26
35
  UnknownFilter:
27
36
  enabled: true
@@ -61,3 +70,12 @@ UndefinedObject:
61
70
 
62
71
  RequiredDirectories:
63
72
  enabled: true
73
+
74
+ DeprecatedFilter:
75
+ enabled: true
76
+
77
+ MissingEnableComment:
78
+ enabled: true
79
+
80
+ ParserBlockingJavaScript:
81
+ enabled: true
@@ -0,0 +1,10 @@
1
+ ---
2
+ Liquid::ColorFilter:
3
+ hex_to_rgba:
4
+ - color_to_rgb
5
+ - color_modify
6
+ Liquid::UrlFilter:
7
+ collection_img_url:
8
+ - img_url
9
+ product_img_url:
10
+ - img_url
@@ -0,0 +1,15 @@
1
+ ---
2
+ - alternative_payment_methods
3
+ - breadcrumb
4
+ - checkout_html_classes
5
+ - checkout_scripts
6
+ - checkout_stylesheets
7
+ - content_for_footer
8
+ - content_for_logo
9
+ - content_for_order_summary
10
+ - direction
11
+ - locale
12
+ - order_summary_toggle
13
+ - page_title
14
+ - skip_to_content_link
15
+ - tracking_code
data/dev.yml CHANGED
@@ -19,5 +19,7 @@ commands:
19
19
  run: bundle exec rake test
20
20
  style:
21
21
  run: bundle exec rake rubocop
22
+ autocorrect:
23
+ run: bundle exec rubocop --auto-correct
22
24
  language-server:
23
25
  run: bundle exec theme-check-language-server
data/lib/theme_check.rb CHANGED
@@ -5,6 +5,7 @@ require_relative "theme_check/analyzer"
5
5
  require_relative "theme_check/check"
6
6
  require_relative "theme_check/checks_tracking"
7
7
  require_relative "theme_check/cli"
8
+ require_relative "theme_check/disabled_checks"
8
9
  require_relative "theme_check/liquid_check"
9
10
  require_relative "theme_check/locale_diff"
10
11
  require_relative "theme_check/json_check"
@@ -17,9 +18,13 @@ require_relative "theme_check/node"
17
18
  require_relative "theme_check/offense"
18
19
  require_relative "theme_check/printer"
19
20
  require_relative "theme_check/shopify_liquid"
21
+ require_relative "theme_check/storage"
22
+ require_relative "theme_check/file_system_storage"
23
+ require_relative "theme_check/in_memory_storage"
20
24
  require_relative "theme_check/tags"
21
25
  require_relative "theme_check/template"
22
26
  require_relative "theme_check/theme"
23
27
  require_relative "theme_check/visitor"
28
+ require_relative "theme_check/corrector"
24
29
 
25
30
  Dir[__dir__ + "/theme_check/checks/*.rb"].each { |file| require file }
@@ -3,9 +3,10 @@ module ThemeCheck
3
3
  class Analyzer
4
4
  attr_reader :offenses
5
5
 
6
- def initialize(theme, checks = Check.all.map(&:new))
6
+ def initialize(theme, checks = Check.all.map(&:new), auto_correct = false)
7
7
  @theme = theme
8
8
  @offenses = []
9
+ @auto_correct = auto_correct
9
10
 
10
11
  @liquid_checks = Checks.new
11
12
  @json_checks = Checks.new
@@ -34,10 +35,19 @@ module ThemeCheck
34
35
  @offenses
35
36
  end
36
37
 
37
- def analyze_file(path)
38
- path = Pathname.new(path)
39
- analyze_theme
40
- @offenses.reject! { |offense| offense.template.path != path }
38
+ def uncorrectable_offenses
39
+ unless @auto_correct
40
+ return @offenses
41
+ end
42
+
43
+ @offenses.select { |offense| !offense.correctable? }
44
+ end
45
+
46
+ def correct_offenses
47
+ if @auto_correct
48
+ @offenses.each(&:correct)
49
+ @theme.liquid.each(&:write)
50
+ end
41
51
  end
42
52
  end
43
53
  end
@@ -50,6 +50,13 @@ module ThemeCheck
50
50
  @doc = doc if doc
51
51
  @doc if defined?(@doc)
52
52
  end
53
+
54
+ def can_disable(disableable = nil)
55
+ unless disableable.nil?
56
+ @can_disable = disableable
57
+ end
58
+ defined?(@can_disable) ? @can_disable : true
59
+ end
53
60
  end
54
61
 
55
62
  def severity
@@ -80,6 +87,10 @@ module ThemeCheck
80
87
  defined?(@ignored) && @ignored
81
88
  end
82
89
 
90
+ def can_disable?
91
+ self.class.can_disable
92
+ end
93
+
83
94
  def to_s
84
95
  s = +"#{code_name}:\n"
85
96
  properties = { severity: severity, category: category, doc: doc }.merge(options)
@@ -8,5 +8,15 @@ module ThemeCheck
8
8
  end
9
9
  end
10
10
  end
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)
20
+ end
11
21
  end
12
22
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module ThemeCheck
3
+ class DeprecatedFilter < LiquidCheck
4
+ doc "https://shopify.dev/docs/themes/liquid/reference/filters/deprecated-filters"
5
+ category :liquid
6
+ severity :suggestion
7
+
8
+ def on_variable(node)
9
+ used_filters = node.value.filters.map { |name, *_rest| name }
10
+ used_filters.each do |filter|
11
+ alternatives = ShopifyLiquid::DeprecatedFilter.alternatives(filter)
12
+ next unless alternatives
13
+
14
+ alternatives = alternatives.map { |alt| "`#{alt}`" }
15
+ add_offense(
16
+ "Deprecated filter `#{filter}`, consider using an alternative: #{alternatives.join(', ')}",
17
+ node: node,
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module ThemeCheck
3
+ class MissingEnableComment < LiquidCheck
4
+ severity :error
5
+
6
+ # Don't allow this check to be disabled with a comment,
7
+ # as we need to be able to check for disabled checks.
8
+ can_disable false
9
+
10
+ def on_document(_node)
11
+ @disabled_checks = DisabledChecks.new
12
+ end
13
+
14
+ def on_comment(node)
15
+ @disabled_checks.update(node)
16
+ end
17
+
18
+ def after_document(node)
19
+ return if @disabled_checks.full_document_disabled?
20
+ return unless @disabled_checks.any?
21
+
22
+ message = if @disabled_checks.all_disabled?
23
+ "All checks were"
24
+ else
25
+ @disabled_checks.all.join(', ') + " " + (@disabled_checks.all.size == 1 ? "was" : "were")
26
+ end
27
+
28
+ add_offense("#{message} disabled but not re-enabled with theme-check-enable", node: node)
29
+ end
30
+ end
31
+ end
@@ -9,21 +9,21 @@ module ThemeCheck
9
9
  category :liquid
10
10
  doc "https://shopify.dev/docs/themes/theme-templates"
11
11
 
12
- LAYOUT_FILENAME = "layout/theme"
13
- REQUIRED_TEMPLATES_FILES = %w(index product collection cart blog article page list-collections search 404
14
- gift_card customers/account customers/activate_account customers/addresses
15
- customers/login customers/order customers/register customers/reset_password password)
12
+ REQUIRED_LIQUID_FILES = %w(layout/theme)
13
+ REQUIRED_TEMPLATE_FILES = %w(
14
+ index product collection cart blog article page list-collections search 404
15
+ gift_card customers/account customers/activate_account customers/addresses
16
+ customers/login customers/order customers/register customers/reset_password password
17
+ )
16
18
  .map { |file| "templates/#{file}" }
17
19
 
18
20
  def on_end
19
- missing_files = (REQUIRED_TEMPLATES_FILES + [LAYOUT_FILENAME]) - theme.liquid.map(&:name)
20
- missing_files.each { |file| add_missing_file_offense(file) }
21
- end
22
-
23
- private
24
-
25
- def add_missing_file_offense(file)
26
- add_offense("Theme is missing '#{file}.liquid' file")
21
+ (REQUIRED_LIQUID_FILES - theme.liquid.map(&:name)).each do |file|
22
+ add_offense("'#{file}.liquid' is missing")
23
+ end
24
+ (REQUIRED_TEMPLATE_FILES - (theme.liquid + theme.json).map(&:name)).each do |file|
25
+ add_offense("'#{file}.liquid' or '#{file}.json' is missing")
26
+ end
27
27
  end
28
28
  end
29
29
  end