theme-check 0.2.0 → 0.3.3

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/.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