theme-check 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.github/probots.yml +3 -0
  3. data/.github/workflows/theme-check.yml +28 -0
  4. data/.gitignore +13 -0
  5. data/.rubocop.yml +18 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/CONTRIBUTING.md +132 -0
  8. data/Gemfile +26 -0
  9. data/Guardfile +7 -0
  10. data/LICENSE.md +8 -0
  11. data/README.md +71 -0
  12. data/Rakefile +14 -0
  13. data/bin/liquid-server +4 -0
  14. data/config/default.yml +63 -0
  15. data/data/shopify_liquid/filters.yml +174 -0
  16. data/data/shopify_liquid/objects.yml +81 -0
  17. data/dev.yml +23 -0
  18. data/docs/preview.png +0 -0
  19. data/exe/theme-check +6 -0
  20. data/exe/theme-check-language-server +12 -0
  21. data/lib/theme_check.rb +25 -0
  22. data/lib/theme_check/analyzer.rb +43 -0
  23. data/lib/theme_check/check.rb +92 -0
  24. data/lib/theme_check/checks.rb +12 -0
  25. data/lib/theme_check/checks/convert_include_to_render.rb +13 -0
  26. data/lib/theme_check/checks/default_locale.rb +12 -0
  27. data/lib/theme_check/checks/liquid_tag.rb +48 -0
  28. data/lib/theme_check/checks/matching_schema_translations.rb +73 -0
  29. data/lib/theme_check/checks/matching_translations.rb +29 -0
  30. data/lib/theme_check/checks/missing_required_template_files.rb +29 -0
  31. data/lib/theme_check/checks/missing_template.rb +25 -0
  32. data/lib/theme_check/checks/nested_snippet.rb +46 -0
  33. data/lib/theme_check/checks/required_directories.rb +24 -0
  34. data/lib/theme_check/checks/required_layout_theme_object.rb +40 -0
  35. data/lib/theme_check/checks/space_inside_braces.rb +58 -0
  36. data/lib/theme_check/checks/syntax_error.rb +29 -0
  37. data/lib/theme_check/checks/template_length.rb +18 -0
  38. data/lib/theme_check/checks/translation_key_exists.rb +35 -0
  39. data/lib/theme_check/checks/undefined_object.rb +86 -0
  40. data/lib/theme_check/checks/unknown_filter.rb +25 -0
  41. data/lib/theme_check/checks/unused_assign.rb +54 -0
  42. data/lib/theme_check/checks/unused_snippet.rb +34 -0
  43. data/lib/theme_check/checks/valid_html_translation.rb +43 -0
  44. data/lib/theme_check/checks/valid_json.rb +14 -0
  45. data/lib/theme_check/checks/valid_schema.rb +13 -0
  46. data/lib/theme_check/checks_tracking.rb +8 -0
  47. data/lib/theme_check/cli.rb +78 -0
  48. data/lib/theme_check/config.rb +108 -0
  49. data/lib/theme_check/json_check.rb +11 -0
  50. data/lib/theme_check/json_file.rb +47 -0
  51. data/lib/theme_check/json_helpers.rb +9 -0
  52. data/lib/theme_check/language_server.rb +11 -0
  53. data/lib/theme_check/language_server/handler.rb +117 -0
  54. data/lib/theme_check/language_server/server.rb +140 -0
  55. data/lib/theme_check/liquid_check.rb +13 -0
  56. data/lib/theme_check/locale_diff.rb +69 -0
  57. data/lib/theme_check/node.rb +117 -0
  58. data/lib/theme_check/offense.rb +104 -0
  59. data/lib/theme_check/parsing_helpers.rb +17 -0
  60. data/lib/theme_check/printer.rb +74 -0
  61. data/lib/theme_check/shopify_liquid.rb +3 -0
  62. data/lib/theme_check/shopify_liquid/filter.rb +18 -0
  63. data/lib/theme_check/shopify_liquid/object.rb +16 -0
  64. data/lib/theme_check/tags.rb +146 -0
  65. data/lib/theme_check/template.rb +73 -0
  66. data/lib/theme_check/theme.rb +60 -0
  67. data/lib/theme_check/version.rb +4 -0
  68. data/lib/theme_check/visitor.rb +37 -0
  69. data/theme-check.gemspec +28 -0
  70. metadata +156 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 037f9fd0dba35bf611162475709aef96e0e24c8e63b18eac3ad20b4d65d37255
4
+ data.tar.gz: b856c24c2135a805c91c230d1c3a2e0dc178d4f0249faa77bb9a2c263993dbd2
5
+ SHA512:
6
+ metadata.gz: 1f4cafab1d1a3adf53771682f0faf00b1a327dc3a358a60157a92a3c7625636ce38eb266a271d01891cae0786c0f4c38c35a3ed03cd34d1b9deb7bb86197ba41
7
+ data.tar.gz: cc0b7f5d9878c74b19a500d93cb873e0e17610134cb99fee832d1c31e20d6e6d805b494c44897e4d590d7b4c9bec99bb95b10b511d3fa0a13d7e1813035071a1
@@ -0,0 +1,3 @@
1
+ # .github/probots.yml
2
+ enabled:
3
+ - cla
@@ -0,0 +1,28 @@
1
+ name: ThemeCheck
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ test:
11
+
12
+ runs-on: ubuntu-20.04
13
+
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: 2.7
20
+ - uses: actions/cache@v1
21
+ with:
22
+ path: vendor/bundle
23
+ key: ${{ runner.os }}-gems-${{ hashFiles('Gemfile') }}
24
+ restore-keys: ${{ runner.os }}-gems-
25
+ - name: Install dependencies
26
+ run: bundle install --jobs=3 --retry=3 --path=vendor/bundle
27
+ - name: Run tests
28
+ run: bundle exec rake
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ *.gem
11
+ Gemfile.lock
12
+
13
+ .rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml
@@ -0,0 +1,18 @@
1
+ inherit_from:
2
+ - 'https://shopify.github.io/ruby-style-guide/rubocop.yml'
3
+
4
+ require: rubocop-performance
5
+
6
+ AllCops:
7
+ TargetRubyVersion: 2.7
8
+ Exclude:
9
+ - 'vendor/bundle/**/*'
10
+
11
+ Metrics/MethodLength:
12
+ Enabled: false
13
+
14
+ Layout/LineLength:
15
+ Enabled: false
16
+
17
+ Lint/MissingSuper:
18
+ Enabled: false
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at opensource@shopify.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
@@ -0,0 +1,132 @@
1
+ # Contributing to Theme Check
2
+
3
+ We love receiving pull requests!
4
+
5
+ ## Standards
6
+
7
+ * Checks should do one thing, and do it well.
8
+ * PR should explain what the feature does, and why the change exists.
9
+ * PR should include any carrier specific documentation explaining how it works.
10
+ * Code _must_ be tested.
11
+ * Be consistent. Write clean code that follows [Ruby community standards](https://github.com/bbatsov/ruby-style-guide).
12
+ * Code should be generic and reusable.
13
+
14
+ ## How to contribute
15
+
16
+ 1. Fork it ( https://github.com/Shopify/theme-check/fork )
17
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
18
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
19
+ 4. Push to the branch (`git push origin my-new-feature`)
20
+ 5. Create a new Pull Request
21
+
22
+ ## Running Tests
23
+
24
+ ```
25
+ bundle install # Or `dev up` if you're from Shopify
26
+ bundle exec rake
27
+ ```
28
+
29
+ ## Checking a theme
30
+
31
+ ```
32
+ bundle exec theme-check /path/to/your/theme
33
+ ```
34
+
35
+ ## Creating a new "Check"
36
+
37
+ Under `lib/theme_check/checks`, create new Ruby file with a unique name describing what you want to check for.
38
+
39
+ ```ruby
40
+ module ThemeCheck
41
+ # Does one thing, and does it well!
42
+ # NOTE: inherit from JsonCheck to implement a JSON based check.
43
+ class MyCheckName < LiquidCheck
44
+ severity :suggestion # :error or :style
45
+ doc "https://..." # Optional link to doc
46
+
47
+ def on_document(node)
48
+ # Called with the root node of all templates
49
+ node.value # is the original Liquid object for this node. See Liquid source code for details.
50
+ node.template # is the template being analyzed, See lib/theme_check/template.rb.
51
+ node.parent # is the parent node.
52
+ node.children # are the children nodes.
53
+ # See lib/theme_check/node.rb for more helper methods
54
+ theme # Gives you access to all the templates in the theme. See lib/theme_check/theme.rb.
55
+ end
56
+
57
+ def on_node(node)
58
+ # Called for every node
59
+ end
60
+
61
+ def on_tag(node)
62
+ # Called for each tag (if, include, for, assign, etc.)
63
+ end
64
+
65
+ def after_tag(node)
66
+ # Called after the tag children have been visited
67
+
68
+ # If you find an issue, add an offense:
69
+ add_offense("Describe the problem...", node: node)
70
+ # Or, if the offense is related to the whole template:
71
+ add_offense("Describe the problem...", template: node.template)
72
+ end
73
+
74
+ def on_assign(node)
75
+ # Called only for {% assign ... %} tags
76
+ end
77
+
78
+ def on_string(node)
79
+ # Called for every `String` (including inside if conditions).
80
+ if node.parent.block?
81
+ # If parent is a block, `node.value` is a String written directly to the output when
82
+ # the template is rendered.
83
+ end
84
+ end
85
+
86
+ def on_error(exception)
87
+ # Called each time a Liquid exception is raised while parsing the template
88
+ end
89
+
90
+ def on_end
91
+ # A special callback after we're done visiting all the templates
92
+ end
93
+
94
+ # Each type of node has a corresponding `on_node_class_name` & `after_node_class_name`
95
+ # A few common examples:
96
+ # on_block_body(node)
97
+ # on_capture(node)
98
+ # on_case(node)
99
+ # on_comment(node)
100
+ # on_condition(node)
101
+ # on_document(node)
102
+ # on_else_condition(node)
103
+ # on_for(node)
104
+ # on_form(node)
105
+ # on_if(node)
106
+ # on_include(node)
107
+ # on_integer(node)
108
+ # on_layout(node)
109
+ # on_method_literal(node)
110
+ # on_paginate(node)
111
+ # on_range(node)
112
+ # on_render(node)
113
+ # on_schema(node)
114
+ # on_section(node)
115
+ # on_style(node)
116
+ # on_unless(node)
117
+ # on_variable(node)
118
+ # on_variable_lookup(node)
119
+ end
120
+ end
121
+ ```
122
+
123
+ Add the new check to `config/default.yml` to enable it.
124
+
125
+ ```yaml
126
+ MyCheckName:
127
+ enabled: true
128
+ ```
129
+
130
+ Add a corresponding test file under `test/checks`.
131
+
132
+ When done, run the tests with `dev test`.
data/Gemfile ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
5
+
6
+ # Specify your gem's dependencies in theme-check.gemspec
7
+ gemspec
8
+
9
+ gem 'bundler'
10
+ gem 'rake'
11
+
12
+ group :test do
13
+ gem 'minitest'
14
+ gem 'minitest-focus'
15
+ gem 'mocha'
16
+ gem 'pry-byebug'
17
+ end
18
+
19
+ group :development do
20
+ gem 'guard'
21
+ gem 'guard-minitest'
22
+ end
23
+
24
+ gem 'rubocop', '~> 0.93.1', require: false
25
+ gem 'rubocop-performance', '~> 1.8.1', require: false
26
+ gem 'rubocop-shopify', '~> 1.0.6', require: false
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ guard :minitest do
4
+ watch(%r{^test/(.*)_test.rb$})
5
+ watch(%r{^lib/theme_check/(.*)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
6
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
7
+ end
@@ -0,0 +1,8 @@
1
+
2
+ Copyright 2020-present, Shopify Inc.
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # Theme Check ✅ - A linter for Themes
2
+
3
+ Think RuboCop, or eslint, but for Shopify themes.
4
+
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
+
7
+ Theme Check is also available [inside some code editors](https://github.com/Shopify/theme-check/wiki).
8
+
9
+ ![](docs/preview.png)
10
+
11
+ _Disclaimer: This tool is not supported as part of the Partners program._
12
+
13
+ ## Supported Checks
14
+
15
+ Theme Check currently checks for the following:
16
+
17
+ ✅ Liquid syntax errors
18
+ ✅ JSON syntax errors
19
+ ✅ Missing snippet & section templates
20
+ ✅ Unused `{% assign ... %}`
21
+ ✅ Unused snippet templates
22
+ ✅ Template length
23
+ ✅ Deprecated tags
24
+ ✅ Unknown tags
25
+ ✅ Unknown filters
26
+ ✅ Missing `{{ content_for_* }}` in `theme.liquid`
27
+ ✅ Excessive nesting of snippets
28
+ ✅ Missing or extra spaces inside `{% ... %}` and `{{ ... }}`
29
+ ✅ Missing default locale file
30
+ ✅ Unmatching translation keys in locale files
31
+ ✅ Using unknown translation keys in `{{ 'missing_key' | t }}`
32
+ ✅ Using several `{% ... %}` instead of `{% liquid ... %}`
33
+ ✅ Undefined [objects](https://shopify.dev/docs/themes/liquid/reference/objects)
34
+
35
+ And many more to come! Suggestions welcome ([create an issue](https://github.com/Shopify/theme-check/issues)).
36
+
37
+ ## Installation
38
+
39
+ ```
40
+ gem install theme-check
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```
46
+ theme-check /path/to/your/theme
47
+
48
+ # or from /path/to/your/theme
49
+ theme-check
50
+ ```
51
+
52
+ Run `theme-check --help` to get full usage.
53
+
54
+ ## Configuration
55
+
56
+ Add a `.theme-check.yml` file at the root of your theme to configure:
57
+
58
+ ```yaml
59
+ # If your theme is not using the supported directory structure, provide the root path
60
+ # where to find the `templates/`, `sections/`, `snippets/` directories as they would
61
+ # be uploaded to Shopify.
62
+ root: dist
63
+
64
+ # Disable some checks
65
+ TemplateLength:
66
+ enabled: false
67
+ # Or configure options
68
+ max_length: 300
69
+ ```
70
+
71
+ See [config/default.yml](config/default.yml) for available options & defaults.
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ require "rake/testtask"
3
+ require "rubocop/rake_task"
4
+ require "bundler/gem_tasks"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ RuboCop::RakeTask.new
13
+
14
+ task default: [:test, :rubocop]
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ dev="/opt/dev/bin/dev"
3
+ cd "$($dev project-path theme-check)"
4
+ "$dev" language-server
@@ -0,0 +1,63 @@
1
+ ConvertIncludeToRender:
2
+ enabled: true
3
+
4
+ LiquidTag:
5
+ enabled: true
6
+
7
+ MissingTemplate:
8
+ enabled: true
9
+
10
+ NestedSnippet:
11
+ enabled: true
12
+
13
+ RequiredLayoutThemeObject:
14
+ enabled: true
15
+
16
+ SpaceInsideBraces:
17
+ enabled: true
18
+
19
+ SyntaxError:
20
+ enabled: true
21
+
22
+ TemplateLength:
23
+ enabled: true
24
+ max_length: 200
25
+
26
+ UnknownFilter:
27
+ enabled: true
28
+
29
+ UnusedAssign:
30
+ enabled: true
31
+
32
+ UnusedSnippet:
33
+ enabled: true
34
+
35
+ MatchingSchemaTranslations:
36
+ enabled: true
37
+
38
+ MatchingTranslations:
39
+ enabled: true
40
+
41
+ DefaultLocale:
42
+ enabled: true
43
+
44
+ TranslationKeyExists:
45
+ enabled: true
46
+
47
+ ValidHTMLTranslation:
48
+ enabled: true
49
+
50
+ ValidJson:
51
+ enabled: true
52
+
53
+ ValidSchema:
54
+ enabled: true
55
+
56
+ MissingRequiredTemplateFiles:
57
+ enabled: true
58
+
59
+ UndefinedObject:
60
+ enabled: true
61
+
62
+ RequiredDirectories:
63
+ enabled: true