theme-check 0.10.0 → 0.10.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +20 -91
- data/Rakefile +31 -0
- data/docs/api/check.md +15 -0
- data/docs/api/html_check.md +46 -0
- data/docs/api/json_check.md +19 -0
- data/docs/api/liquid_check.md +99 -0
- data/docs/checks/{CHECK_DOCS_TEMPLATE.md → TEMPLATE.md.erb} +5 -5
- data/lib/theme_check/check.rb +1 -1
- data/lib/theme_check/checks/TEMPLATE.rb.erb +11 -0
- data/lib/theme_check/json_file.rb +5 -0
- data/lib/theme_check/language_server/diagnostics_tracker.rb +2 -0
- data/lib/theme_check/offense.rb +5 -5
- data/lib/theme_check/template.rb +1 -0
- data/lib/theme_check/version.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56b6a91e5ac4b1e2045b1e4fb2748e5ecb9600206f9ba07df401c1410bf494fc
|
4
|
+
data.tar.gz: b6fb1b3957ae5aecd7b2efbaa76a2e3c07390ec442f94d416ea5b20ca2cbf966
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2ca68b31968949dc52240c8ee5ca607e10e063b500cda0a7b06306589849f2bc0098f1b0742f962a91a0ca282aa504acb9f7c0de6b785afff0b577b84fa6eba
|
7
|
+
data.tar.gz: c7186299a1168c5ad1e3cd6544ee8c8248b96a492e1dc82481c26a69e75fb1f04172ceb58da61e310a393fd36c9b990464542e7528810c7bea556b5b8ce9b61d
|
data/CHANGELOG.md
CHANGED
data/CONTRIBUTING.md
CHANGED
@@ -36,103 +36,32 @@ bundle exec theme-check /path/to/your/theme
|
|
36
36
|
|
37
37
|
## Creating a new "Check"
|
38
38
|
|
39
|
-
|
39
|
+
Run `bundle exec rake "new_check[MyNewCheckName]"` to generate all the files required to create a new check.
|
40
40
|
|
41
|
-
|
42
|
-
module ThemeCheck
|
43
|
-
# Does one thing, and does it well!
|
44
|
-
# NOTE: inherit from `JsonCheck` to implement a JSON-based check, and from `HtmlCheck`
|
45
|
-
# to implement an HTML-based one. See other checks in `lib/theme_check/checks` for examples.
|
46
|
-
class MyCheckName < LiquidCheck
|
47
|
-
severity :suggestion # :error or :style
|
48
|
-
|
49
|
-
def on_document(node)
|
50
|
-
# Called with the root node of all templates
|
51
|
-
node.value # is the original Liquid object for this node. See Liquid source code for details.
|
52
|
-
node.template # is the template being analyzed, See lib/theme_check/template.rb.
|
53
|
-
node.parent # is the parent node.
|
54
|
-
node.children # are the children nodes.
|
55
|
-
# See lib/theme_check/node.rb for more helper methods
|
56
|
-
theme # Gives you access to all the templates in the theme. See lib/theme_check/theme.rb.
|
57
|
-
end
|
58
|
-
|
59
|
-
def on_node(node)
|
60
|
-
# Called for every node
|
61
|
-
end
|
62
|
-
|
63
|
-
def on_tag(node)
|
64
|
-
# Called for each tag (if, include, for, assign, etc.)
|
65
|
-
end
|
66
|
-
|
67
|
-
def after_tag(node)
|
68
|
-
# Called after the tag children have been visited
|
69
|
-
|
70
|
-
# If you find an issue, add an offense:
|
71
|
-
add_offense("Describe the problem...", node: node)
|
72
|
-
# Or, if the offense is related to the whole template:
|
73
|
-
add_offense("Describe the problem...", template: node.template)
|
74
|
-
end
|
75
|
-
|
76
|
-
def on_assign(node)
|
77
|
-
# Called only for {% assign ... %} tags
|
78
|
-
end
|
79
|
-
|
80
|
-
def on_string(node)
|
81
|
-
# Called for every `String` (including inside if conditions).
|
82
|
-
if node.parent.block?
|
83
|
-
# If parent is a block, `node.value` is a String written directly to the output when
|
84
|
-
# the template is rendered.
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def on_error(exception)
|
89
|
-
# Called each time a Liquid exception is raised while parsing the template
|
90
|
-
end
|
91
|
-
|
92
|
-
def on_end
|
93
|
-
# A special callback after we're done visiting all the templates
|
94
|
-
end
|
95
|
-
|
96
|
-
# Each type of node has a corresponding `on_node_class_name` & `after_node_class_name`
|
97
|
-
# A few common examples:
|
98
|
-
# on_block_body(node)
|
99
|
-
# on_capture(node)
|
100
|
-
# on_case(node)
|
101
|
-
# on_comment(node)
|
102
|
-
# on_condition(node)
|
103
|
-
# on_document(node)
|
104
|
-
# on_else_condition(node)
|
105
|
-
# on_for(node)
|
106
|
-
# on_form(node)
|
107
|
-
# on_if(node)
|
108
|
-
# on_include(node)
|
109
|
-
# on_integer(node)
|
110
|
-
# on_layout(node)
|
111
|
-
# on_method_literal(node)
|
112
|
-
# on_paginate(node)
|
113
|
-
# on_range(node)
|
114
|
-
# on_render(node)
|
115
|
-
# on_schema(node)
|
116
|
-
# on_section(node)
|
117
|
-
# on_style(node)
|
118
|
-
# on_unless(node)
|
119
|
-
# on_variable(node)
|
120
|
-
# on_variable_lookup(node)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
```
|
41
|
+
Check the [Check API](/docs/api/check.md) for how to implement a check. Also take a look at other checks in [lib/theme_check/checks](/lib/theme_check/checks).
|
124
42
|
|
125
|
-
|
43
|
+
We done implementing your check, add it to `config/default.yml` to enable it:
|
126
44
|
|
127
45
|
```yaml
|
128
|
-
|
46
|
+
MyNewCheckName:
|
129
47
|
enabled: true
|
48
|
+
ignore: []
|
130
49
|
```
|
131
50
|
|
132
|
-
|
133
|
-
|
134
|
-
Add a documentation file in `docs/checks/#{name_of_check}.md` based off of the [check documentation template][doctemplate].
|
51
|
+
If the check is configurable, the `initialize` argument names and default values should also be duplicated inside `config/default.yml`. eg.:
|
135
52
|
|
136
|
-
|
53
|
+
```ruby
|
54
|
+
class MyCheckName < LiquidCheck
|
55
|
+
def initialize(muffin_mode: true)
|
56
|
+
@muffin_mode = muffin_mode
|
57
|
+
end
|
58
|
+
# ...
|
59
|
+
end
|
60
|
+
```
|
137
61
|
|
138
|
-
|
62
|
+
```yaml
|
63
|
+
MyNewCheckName:
|
64
|
+
enabled: true
|
65
|
+
ignore: []
|
66
|
+
muffin_mode: true
|
67
|
+
```
|
data/Rakefile
CHANGED
@@ -52,3 +52,34 @@ task :prerelease, [:version] do |_t, args|
|
|
52
52
|
require 'theme_check/releaser'
|
53
53
|
ThemeCheck::Releaser.new.release(args.version)
|
54
54
|
end
|
55
|
+
|
56
|
+
desc "Create a new check"
|
57
|
+
task :new_check, [:name] do |_t, args|
|
58
|
+
require "theme_check/string_helpers"
|
59
|
+
class_name = args.name
|
60
|
+
base_name = ThemeCheck::StringHelpers.underscore(class_name)
|
61
|
+
code_source = "lib/theme_check/checks/#{base_name}.rb"
|
62
|
+
doc_source = "docs/checks/#{base_name}.md"
|
63
|
+
test_source = "test/checks/#{base_name}_test.rb"
|
64
|
+
erb(
|
65
|
+
"lib/theme_check/checks/TEMPLATE.rb.erb", code_source,
|
66
|
+
class_name: class_name,
|
67
|
+
)
|
68
|
+
erb(
|
69
|
+
"test/checks/TEMPLATE.rb.erb", test_source,
|
70
|
+
class_name: class_name,
|
71
|
+
)
|
72
|
+
erb(
|
73
|
+
"docs/checks/TEMPLATE.md.erb", doc_source,
|
74
|
+
class_name: class_name,
|
75
|
+
code_source: code_source,
|
76
|
+
doc_source: doc_source,
|
77
|
+
)
|
78
|
+
sh "bundle exec ruby -Itest test/checks/my_new_check_test.rb"
|
79
|
+
end
|
80
|
+
|
81
|
+
def erb(file, to, **args)
|
82
|
+
require "erb"
|
83
|
+
File.write(to, ERB.new(File.read(file)).result_with_hash(args))
|
84
|
+
puts "Generated #{to}"
|
85
|
+
end
|
data/docs/api/check.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Check API
|
2
|
+
|
3
|
+
Theme Check uses static analysis. It parses theme files into an AST, and then calls the appropriate checks to analyze it.
|
4
|
+
|
5
|
+
An [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) is a tree of node, representing the theme file.
|
6
|
+
|
7
|
+
Checks are Ruby classes with callback methods:
|
8
|
+
- `on_TYPE` that runs before a node of the specific TYPE is visited.
|
9
|
+
- `after_TYPE` that runs after a node of the specific TYPE is visited.
|
10
|
+
|
11
|
+
There are three types of checks currently supported:
|
12
|
+
|
13
|
+
- [`LiquidCheck`](/docs/api/liquid_check.md)
|
14
|
+
- [`HtmlCheck`](/docs/api/html_check.md)
|
15
|
+
- [`JsonCheck`](/docs/api/html_check.md)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# HTML check API
|
2
|
+
|
3
|
+
For checking HTML elements in `.liquid` files.
|
4
|
+
|
5
|
+
If you need to check an HTML tag or its attributes, use an `HtmlCheck`.
|
6
|
+
|
7
|
+
The HTML in Liquid files is parsed using the Nokogiri, by consequence you will get [`Nokogiri::XML::Node`][nokogiri].
|
8
|
+
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
module ThemeCheck
|
12
|
+
class MyCheckName < HtmlCheck
|
13
|
+
category :html,
|
14
|
+
# A check can belong to multiple categories. Valid ones:
|
15
|
+
categories :translation, :performance
|
16
|
+
severity :suggestion # :error or :style
|
17
|
+
|
18
|
+
def on_document(node)
|
19
|
+
# Called with the root node of all templates
|
20
|
+
node.value # is an instance of Nokogiri::XML::Node
|
21
|
+
node.template # is the template being analyzed, See lib/theme_check/template.rb.
|
22
|
+
node.parent # is the parent node.
|
23
|
+
node.children # are the children nodes.
|
24
|
+
# See lib/theme_check/html_node.rb for more helper methods
|
25
|
+
theme # Gives you access to all the templates in the theme. See lib/theme_check/theme.rb.
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_img(node)
|
29
|
+
# Called for every <img> element in the file.
|
30
|
+
node.attrbutes["class"] # Get the class attribute of the img element.
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_a(node)
|
34
|
+
# Called for every <a> element in the file.
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
## Resources
|
41
|
+
|
42
|
+
- [Nokogiri::XML::Node API doc][nokogiri]
|
43
|
+
|
44
|
+
[nokogiri]: https://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node
|
45
|
+
|
46
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# JSON check API
|
2
|
+
|
3
|
+
For checking the content of `.json` files.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
module ThemeCheck
|
7
|
+
class MyCheckName < JsonCheck
|
8
|
+
category :json,
|
9
|
+
# A check can belong to multiple categories. Valid ones:
|
10
|
+
categories :translation, :performance
|
11
|
+
severity :suggestion # :error or :style
|
12
|
+
|
13
|
+
def on_file(file)
|
14
|
+
file # an instance of `ThemeCheck::JsonFile`
|
15
|
+
file.content # the parsed JSON, as a Ruby object, usually a Hash
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
```
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Liquid check API
|
2
|
+
|
3
|
+
For checking the Liquid code in `.liquid` files.
|
4
|
+
|
5
|
+
All code inside `{% ... %}` or `{{ ... }}` is Liquid code.
|
6
|
+
|
7
|
+
Liquid files are parsed using the Liquid parser, by consequence you will get Liquid nodes (tags, blocks) in your callback methods. Check the Liquid source for details on those nodes: [Liquid source][liquidsource].
|
8
|
+
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
module ThemeCheck
|
12
|
+
class MyCheckName < LiquidCheck
|
13
|
+
category :liquid,
|
14
|
+
# A check can belong to multiple categories. Valid ones:
|
15
|
+
categories :translation, :performance
|
16
|
+
severity :suggestion # :error or :style
|
17
|
+
|
18
|
+
def on_document(node)
|
19
|
+
# Called with the root node of all templates
|
20
|
+
node.value # is the original Liquid object for this node. See Liquid source code for details.
|
21
|
+
node.template # is the template being analyzed, See lib/theme_check/template.rb.
|
22
|
+
node.parent # is the parent node.
|
23
|
+
node.children # are the children nodes.
|
24
|
+
# See lib/theme_check/node.rb for more helper methods
|
25
|
+
theme # Gives you access to all the templates in the theme. See lib/theme_check/theme.rb.
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_node(node)
|
29
|
+
# Called for every node
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_tag(node)
|
33
|
+
# Called for each tag (if, include, for, assign, etc.)
|
34
|
+
end
|
35
|
+
|
36
|
+
def after_tag(node)
|
37
|
+
# Called after the tag children have been visited
|
38
|
+
|
39
|
+
# If you find an issue, add an offense:
|
40
|
+
add_offense("Describe the problem...", node: node)
|
41
|
+
# Or, if the offense is related to the whole template:
|
42
|
+
add_offense("Describe the problem...", template: node.template)
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_assign(node)
|
46
|
+
# Called only for {% assign ... %} tags
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_string(node)
|
50
|
+
# Called for every `String` (including inside if conditions).
|
51
|
+
if node.parent.block?
|
52
|
+
# If parent is a block, `node.value` is a String written directly to the output when
|
53
|
+
# the template is rendered.
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def on_variable(node)
|
58
|
+
# Called for each {{ ... }}
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_error(exception)
|
62
|
+
# Called each time a Liquid exception is raised while parsing the template
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_end
|
66
|
+
# A special callback after we're done visiting all the files of the theme
|
67
|
+
end
|
68
|
+
|
69
|
+
# Each type of node has a corresponding `on_node_class_name` & `after_node_class_name`
|
70
|
+
# A few common examples:
|
71
|
+
# on_capture(node)
|
72
|
+
# on_case(node)
|
73
|
+
# on_comment(node)
|
74
|
+
# on_if(node)
|
75
|
+
# on_condition(node)
|
76
|
+
# on_else_condition(node)
|
77
|
+
# on_for(node)
|
78
|
+
# on_form(node)
|
79
|
+
# on_include(node)
|
80
|
+
# on_integer(node)
|
81
|
+
# on_layout(node)
|
82
|
+
# on_method_literal(node)
|
83
|
+
# on_paginate(node)
|
84
|
+
# on_range(node)
|
85
|
+
# on_render(node)
|
86
|
+
# on_schema(node)
|
87
|
+
# on_section(node)
|
88
|
+
# on_style(node)
|
89
|
+
# on_unless(node)
|
90
|
+
# on_variable_lookup(node)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
## Resources
|
96
|
+
|
97
|
+
- [Liquid source][liquidsource]
|
98
|
+
|
99
|
+
[liquidsource]: https://github.com/Shopify/liquid/tree/master/lib/liquid
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Check Title (
|
1
|
+
# Check Title (`<%= class_name %>`)
|
2
2
|
|
3
3
|
A brief paragraph explaining why the check exists.
|
4
4
|
|
@@ -21,7 +21,7 @@ This check is aimed at eliminating ...
|
|
21
21
|
The default configuration for this check is the following:
|
22
22
|
|
23
23
|
```yaml
|
24
|
-
|
24
|
+
<%= class_name %>:
|
25
25
|
enabled: true
|
26
26
|
some_option: 10
|
27
27
|
```
|
@@ -36,12 +36,12 @@ If you don't want to ..., then it's safe to disable this rule.
|
|
36
36
|
|
37
37
|
## Version
|
38
38
|
|
39
|
-
This check has been introduced in Theme Check
|
39
|
+
This check has been introduced in Theme Check THEME_CHECK_VERSION.
|
40
40
|
|
41
41
|
## Resources
|
42
42
|
|
43
43
|
- [Rule Source][codesource]
|
44
44
|
- [Documentation Source][docsource]
|
45
45
|
|
46
|
-
[codesource]:
|
47
|
-
[docsource]:
|
46
|
+
[codesource]: /<%= code_source %>
|
47
|
+
[docsource]: /<%= doc_source %>
|
data/lib/theme_check/check.rb
CHANGED
@@ -18,7 +18,6 @@ module ThemeCheck
|
|
18
18
|
CATEGORIES = [
|
19
19
|
:liquid,
|
20
20
|
:translation,
|
21
|
-
:performance,
|
22
21
|
:html,
|
23
22
|
:json,
|
24
23
|
:performance,
|
@@ -124,6 +123,7 @@ module ThemeCheck
|
|
124
123
|
def ==(other)
|
125
124
|
other.is_a?(Check) && code_name == other.code_name
|
126
125
|
end
|
126
|
+
alias_method :eql?, :==
|
127
127
|
|
128
128
|
def to_s
|
129
129
|
s = +"#{code_name}:\n"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ThemeCheck
|
3
|
+
# TODO: inherit from HtmlCheck or JsonCheck if working on a non-Liquid check
|
4
|
+
class <%= class_name %> < LiquidCheck
|
5
|
+
severity :suggestion
|
6
|
+
category :liquid
|
7
|
+
doc docs_url(__FILE__)
|
8
|
+
|
9
|
+
# TODO: def on_<NODE_TYPE>
|
10
|
+
end
|
11
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "logger"
|
2
3
|
|
3
4
|
module ThemeCheck
|
4
5
|
module LanguageServer
|
@@ -16,6 +17,7 @@ module ThemeCheck
|
|
16
17
|
def build_diagnostics(offenses, analyzed_files: nil)
|
17
18
|
reported_files = Set.new
|
18
19
|
new_single_file_offenses = {}
|
20
|
+
analyzed_files = analyzed_files.map { |path| Pathname.new(path) } if analyzed_files
|
19
21
|
|
20
22
|
offenses.group_by(&:template).each do |template, template_offenses|
|
21
23
|
next unless template
|
data/lib/theme_check/offense.rb
CHANGED
@@ -124,13 +124,13 @@ module ThemeCheck
|
|
124
124
|
|
125
125
|
def ==(other)
|
126
126
|
other.is_a?(Offense) &&
|
127
|
-
|
127
|
+
code_name == other.code_name &&
|
128
128
|
message == other.message &&
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
line_number == other.line_number
|
129
|
+
location == other.location &&
|
130
|
+
start_index == other.start_index &&
|
131
|
+
end_index == other.end_index
|
133
132
|
end
|
133
|
+
alias_method :eql?, :==
|
134
134
|
|
135
135
|
def to_s
|
136
136
|
if template
|
data/lib/theme_check/template.rb
CHANGED
data/lib/theme_check/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: theme-check
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc-André Cournoyer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-06-
|
11
|
+
date: 2021-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -69,7 +69,11 @@ files:
|
|
69
69
|
- data/shopify_liquid/tags.yml
|
70
70
|
- data/shopify_translation_keys.yml
|
71
71
|
- dev.yml
|
72
|
-
- docs/
|
72
|
+
- docs/api/check.md
|
73
|
+
- docs/api/html_check.md
|
74
|
+
- docs/api/json_check.md
|
75
|
+
- docs/api/liquid_check.md
|
76
|
+
- docs/checks/TEMPLATE.md.erb
|
73
77
|
- docs/checks/asset_size_css.md
|
74
78
|
- docs/checks/asset_size_javascript.md
|
75
79
|
- docs/checks/asset_url_filters.md
|
@@ -111,6 +115,7 @@ files:
|
|
111
115
|
- lib/theme_check/bug.rb
|
112
116
|
- lib/theme_check/check.rb
|
113
117
|
- lib/theme_check/checks.rb
|
118
|
+
- lib/theme_check/checks/TEMPLATE.rb.erb
|
114
119
|
- lib/theme_check/checks/asset_size_css.rb
|
115
120
|
- lib/theme_check/checks/asset_size_javascript.rb
|
116
121
|
- lib/theme_check/checks/asset_url_filters.rb
|