theme-check 0.2.0 → 0.3.3

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/CHANGELOG.md +33 -0
  4. data/CONTRIBUTING.md +2 -0
  5. data/README.md +45 -2
  6. data/RELEASING.md +41 -0
  7. data/Rakefile +24 -4
  8. data/config/default.yml +16 -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 +0 -6
  13. data/lib/theme_check/check.rb +11 -0
  14. data/lib/theme_check/checks.rb +10 -0
  15. data/lib/theme_check/checks/missing_enable_comment.rb +31 -0
  16. data/lib/theme_check/checks/parser_blocking_javascript.rb +55 -0
  17. data/lib/theme_check/checks/space_inside_braces.rb +1 -0
  18. data/lib/theme_check/checks/template_length.rb +11 -3
  19. data/lib/theme_check/checks/undefined_object.rb +27 -6
  20. data/lib/theme_check/checks/unused_assign.rb +4 -3
  21. data/lib/theme_check/checks/valid_html_translation.rb +2 -2
  22. data/lib/theme_check/cli.rb +9 -1
  23. data/lib/theme_check/config.rb +95 -43
  24. data/lib/theme_check/corrector.rb +0 -4
  25. data/lib/theme_check/disabled_checks.rb +77 -0
  26. data/lib/theme_check/file_system_storage.rb +51 -0
  27. data/lib/theme_check/in_memory_storage.rb +37 -0
  28. data/lib/theme_check/json_file.rb +12 -10
  29. data/lib/theme_check/language_server/handler.rb +38 -13
  30. data/lib/theme_check/language_server/server.rb +2 -2
  31. data/lib/theme_check/offense.rb +3 -1
  32. data/lib/theme_check/shopify_liquid/object.rb +6 -0
  33. data/lib/theme_check/storage.rb +25 -0
  34. data/lib/theme_check/template.rb +26 -21
  35. data/lib/theme_check/theme.rb +14 -9
  36. data/lib/theme_check/version.rb +1 -1
  37. data/lib/theme_check/visitor.rb +14 -3
  38. data/packaging/homebrew/theme_check.base.rb +10 -6
  39. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4001e286b9a0c851493de76250770565088d1d8f646a6e592ee3f2c142b47c3c
4
- data.tar.gz: 3c3cd833684e0020211365eec091b70be3a064ae72f4c1d7337908fbfdbdacd8
3
+ metadata.gz: b17d14eb5abc8211b6b6ae9f3a676c44770775821a87ce0909412d3eddb81c35
4
+ data.tar.gz: 7bcad0d34832338dd8cd83f220b14c45b061b84b81c5ae2b3a18805aeb9400eb
5
5
  SHA512:
6
- metadata.gz: 35855ff5a3e10f8a8dc60cc40d619fe713f013eb8ee64f92c565c34e3a3b58b9646ca1b707535f777e53cabae1dd58facc2adc4722e5c704b11ff70d9407e959
7
- data.tar.gz: fb9f94add954ec84a0fb8ca6616e8d91981701ff0d62f5dc89692dcd4ddf7715073db531dc81ec7dd1c8a4fc8df93360adbc32ee02254d8538a027236995cbdf
6
+ metadata.gz: 241dfdae6ec38c560ca4697c4b9256d4ceaed0300e9a575c3c92e864ea4fa2fd56420efd87ec2c0f744240fe9db5c6cc7df24c1da8d9606fab020bdd1d630450
7
+ data.tar.gz: bbb52dcf01cf1dcbc3f73fe90d822b9ee22407a31525aab314ad0121e1bd9f52c786b6528594e5eb6224d97e567108bd7962a8c7a67d6e17702a976bab3edb1a
data/.rubocop.yml CHANGED
@@ -7,6 +7,7 @@ AllCops:
7
7
  TargetRubyVersion: 2.7
8
8
  Exclude:
9
9
  - 'vendor/bundle/**/*'
10
+ - 'packaging/builds/**/*'
10
11
 
11
12
  Metrics/MethodLength:
12
13
  Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+
2
+ v0.3.3 / 2021-02-18
3
+ ==================
4
+
5
+ * Fix column_end issues ([#164](https://github.com/Shopify/theme-check/issues/164))
6
+ * Fix stack overflow in UndefinedObject + UnusedAssign when snippets renders itself ([#165](https://github.com/Shopify/theme-check/issues/165))
7
+
8
+ v0.3.2 / 2021-02-17
9
+ ==================
10
+
11
+ * Ignore snippets in UndefinedObject check
12
+
13
+ v0.3.1 / 2021-02-16
14
+ ===================
15
+
16
+ * Fixup version flag
17
+
18
+ v0.3.0 / 2021-02-16
19
+ ===================
20
+
21
+ * Add ParserBlockingJavaScript Check ([#78](https://github.com/Shopify/theme-check/issues/78), [#146](https://github.com/Shopify/theme-check/issues/146))
22
+ * Internal refactor to enable running theme-check in servers ([#145](https://github.com/Shopify/theme-check/issues/145), [#148](https://github.com/Shopify/theme-check/issues/148))
23
+ * Add -v, --version flag ([#126](https://github.com/Shopify/theme-check/issues/126))
24
+ * Exclude content of {% schema %} in line count for TemplateLength ([#140](https://github.com/Shopify/theme-check/issues/140))
25
+ * Fix Language Server removed files bug ([#136](https://github.com/Shopify/theme-check/issues/136))
26
+ * Add ignore config ([#147](https://github.com/Shopify/theme-check/issues/147))
27
+ * Add ability to disable checks with comments ([#79](https://github.com/Shopify/theme-check/issues/79))
28
+ * Adding checks for shopify plus objects in checkout ([#121](https://github.com/Shopify/theme-check/issues/121))
29
+
30
+ v0.2.2 / 2021-01-22
31
+ ===================
32
+
33
+ * [Language Server] Send empty dianogstics to flush errors
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
@@ -4,7 +4,7 @@ Think RuboCop, or eslint, but for Shopify themes.
4
4
 
5
5
  Theme Check is a command line tool that helps you follow Shopify Themes & Liquid best practices by analyzing the Liquid & JSON inside your theme.
6
6
 
7
- Code editor support coming soon!
7
+ Theme Check is also available [inside some code editors](https://github.com/Shopify/theme-check/wiki).
8
8
 
9
9
  ![](docs/preview.png)
10
10
 
@@ -30,8 +30,9 @@ 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
34
  ✅ Deprecated filters
35
+ ✅ Missing `theme-check-enable` comment
35
36
 
36
37
  And many more to come! Suggestions welcome ([create an issue](https://github.com/Shopify/theme-check/issues)).
37
38
 
@@ -79,11 +80,53 @@ Add a `.theme-check.yml` file at the root of your theme to configure:
79
80
  # be uploaded to Shopify.
80
81
  root: dist
81
82
 
83
+ # It is possible to extend theme-check with custom checks
84
+ require:
85
+ - ./path/to/my_custom_check.rb
86
+
82
87
  # Disable some checks
83
88
  TemplateLength:
84
89
  enabled: false
85
90
  # Or configure options
86
91
  max_length: 300
92
+
93
+ # Enable a custom check
94
+ MyCustomCheck
95
+ enabled: true
87
96
  ```
88
97
 
89
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.rb ../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,32 @@ 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]
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
@@ -58,9 +67,16 @@ MissingRequiredTemplateFiles:
58
67
 
59
68
  UndefinedObject:
60
69
  enabled: true
70
+ exclude_snippets: true
61
71
 
62
72
  RequiredDirectories:
63
73
  enabled: true
64
74
 
65
75
  DeprecatedFilter:
66
76
  enabled: true
77
+
78
+ MissingEnableComment:
79
+ enabled: true
80
+
81
+ ParserBlockingJavaScript:
82
+ enabled: true
@@ -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,10 +18,14 @@ 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"
24
28
  require_relative "theme_check/corrector"
29
+ require_relative "theme_check/version"
25
30
 
26
31
  Dir[__dir__ + "/theme_check/checks/*.rb"].each { |file| require file }
@@ -35,12 +35,6 @@ module ThemeCheck
35
35
  @offenses
36
36
  end
37
37
 
38
- def analyze_file(path)
39
- path = Pathname.new(path)
40
- analyze_theme
41
- @offenses.reject! { |offense| offense.template.path != path }
42
- end
43
-
44
38
  def uncorrectable_offenses
45
39
  unless @auto_correct
46
40
  return @offenses
@@ -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,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
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+ module ThemeCheck
3
+ # Reports errors when trying to use parser-blocking script tags
4
+ class ParserBlockingJavaScript < LiquidCheck
5
+ severity :error
6
+ category :liquid
7
+
8
+ PARSER_BLOCKING_SCRIPT_TAG = %r{
9
+ <script # Find the start of a script tag
10
+ (?=(?:[^>]|\n|\r)+?src=)+? # Make sure src= is in the script with a lookahead
11
+ (?:(?!defer|async|type=["']module['"]).)*? # Find tags that don't have defer|async|type="module"
12
+ >
13
+ }xim
14
+ SCRIPT_TAG_FILTER = /\{\{[^}]+script_tag\s+\}\}/
15
+
16
+ def on_document(node)
17
+ @source = node.template.source
18
+ @node = node
19
+ record_offenses
20
+ end
21
+
22
+ private
23
+
24
+ def record_offenses
25
+ record_offenses_from_regex(
26
+ message: "Missing async or defer attribute on script tag",
27
+ regex: PARSER_BLOCKING_SCRIPT_TAG,
28
+ )
29
+ record_offenses_from_regex(
30
+ message: "The script_tag filter is parser-blocking. Use a script tag with the async or defer attribute for better performance",
31
+ regex: SCRIPT_TAG_FILTER,
32
+ )
33
+ end
34
+
35
+ # The trickiness here is matching on scripts that are defined on
36
+ # multiple lines (or repeat matches). This makes the line_number
37
+ # calculation a bit weird. So instead, we traverse the string in
38
+ # a very imperative way.
39
+ def record_offenses_from_regex(regex: nil, message: nil)
40
+ i = 0
41
+ while (i = @source.index(regex, i))
42
+ script = @source.match(regex, i)[0]
43
+
44
+ add_offense(
45
+ message,
46
+ node: @node,
47
+ markup: script,
48
+ line_number: @source[0...i].count("\n") + 1
49
+ )
50
+
51
+ i += script.size
52
+ end
53
+ end
54
+ end
55
+ end