theme-check 0.9.0 → 1.0.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -0
  3. data/CONTRIBUTING.md +20 -91
  4. data/Rakefile +31 -0
  5. data/config/default.yml +31 -3
  6. data/data/shopify_liquid/objects.yml +2 -0
  7. data/docs/api/check.md +15 -0
  8. data/docs/api/html_check.md +46 -0
  9. data/docs/api/json_check.md +19 -0
  10. data/docs/api/liquid_check.md +99 -0
  11. data/docs/checks/{CHECK_DOCS_TEMPLATE.md → TEMPLATE.md.erb} +5 -5
  12. data/docs/checks/asset_size_css_stylesheet_tag.md +50 -0
  13. data/docs/checks/asset_url_filters.md +56 -0
  14. data/docs/checks/deprecate_bgsizes.md +66 -0
  15. data/docs/checks/deprecate_lazysizes.md +61 -0
  16. data/docs/checks/html_parsing_error.md +50 -0
  17. data/docs/checks/img_lazy_loading.md +61 -0
  18. data/docs/checks/liquid_tag.md +2 -2
  19. data/docs/checks/template_length.md +12 -2
  20. data/lib/theme_check/check.rb +1 -1
  21. data/lib/theme_check/checks/TEMPLATE.rb.erb +11 -0
  22. data/lib/theme_check/checks/asset_size_css.rb +11 -74
  23. data/lib/theme_check/checks/asset_size_css_stylesheet_tag.rb +24 -0
  24. data/lib/theme_check/checks/asset_size_javascript.rb +10 -36
  25. data/lib/theme_check/checks/asset_url_filters.rb +46 -0
  26. data/lib/theme_check/checks/deprecate_bgsizes.rb +14 -0
  27. data/lib/theme_check/checks/deprecate_lazysizes.rb +16 -0
  28. data/lib/theme_check/checks/html_parsing_error.rb +12 -0
  29. data/lib/theme_check/checks/img_lazy_loading.rb +20 -0
  30. data/lib/theme_check/checks/liquid_tag.rb +2 -2
  31. data/lib/theme_check/checks/remote_asset.rb +23 -79
  32. data/lib/theme_check/checks/template_length.rb +18 -4
  33. data/lib/theme_check/html_check.rb +2 -0
  34. data/lib/theme_check/html_node.rb +4 -0
  35. data/lib/theme_check/html_visitor.rb +5 -1
  36. data/lib/theme_check/json_file.rb +5 -0
  37. data/lib/theme_check/language_server/diagnostics_tracker.rb +2 -0
  38. data/lib/theme_check/liquid_check.rb +0 -11
  39. data/lib/theme_check/offense.rb +5 -5
  40. data/lib/theme_check/parsing_helpers.rb +3 -1
  41. data/lib/theme_check/regex_helpers.rb +17 -0
  42. data/lib/theme_check/tags.rb +37 -0
  43. data/lib/theme_check/template.rb +1 -0
  44. data/lib/theme_check/version.rb +1 -1
  45. metadata +21 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5970832846bda5a0e7cb27c99ebef32382050c911350d1d208211849bef7d55
4
- data.tar.gz: 89d3bb2d5a261c817dddfbf50ad9ac31a589f88fe4f2a2df23b8c5aaad51c4db
3
+ metadata.gz: 94b918e53777cfb70c65dacd9d9bfbdb6f4463597c8f9c81969e1baef89df7a1
4
+ data.tar.gz: 7e45af9a3dfeab1eafdff5533df68a9e0d67007d94661a2c974c0c2fece61095
5
5
  SHA512:
6
- metadata.gz: 2ee4f610dc461c6e70e870723f801ddb0f1e5381eebfc6622030af68486c2c6650c11e530be38456cf0df8666ecc5e78b26d16cf60428142e04f0ebde45edf9f
7
- data.tar.gz: 36053d9337a817da4334a5341be1877f04d86b3f4d88606a2acccb2028c0a3a07227086183de5aa06bd44e9beeebd0a4650d08b746fdfbced854e945469d61a3
6
+ metadata.gz: ea1b75ca11a66aefe5eef421f577d53716cb180799863185d36fad21a9033b5cf52579df3e593e41867513da410adc7d16de723f35b3a5435e50098b7af2a348
7
+ data.tar.gz: ceab091f6bf7317d899cb5459601b7dfb5294e6e4be962a421b33bad33c74fd730001dea90687f81f754d57e5cdedc5b39d0efb5d95d6c016ed965f7e4e86a9f
data/CHANGELOG.md CHANGED
@@ -1,4 +1,41 @@
1
1
 
2
+ v1.0.0 / 2021-06-28
3
+ ==================
4
+
5
+ * Convert `AssetSizeCSS` to `HtmlCheck`
6
+ * Add `DeprecateLazysizes` & `DeprecateBgsizes` checks
7
+ * Allow hardcoded CDN urls in `RemoteAsset`
8
+ * Bump `LiquidTag` `min_consecutive_statements` default to 5
9
+ * Exclude {% javascript %} and {% stylesheet %} from line counts in `TemplateLength`
10
+ * Bump `TemplateLength` `max_length` default to 500
11
+ * Fix `StringScanner#skip(String)` not being supported on some Rubies
12
+ * Fix `ParsingHelpers#outside_of_strings` handling of empty strings
13
+ * Update to support new `{% render %}` syntax
14
+ * Converted `AssetSizeJavaScript` to `HtmlCheck`
15
+
16
+ v0.10.2 / 2021-06-18
17
+ ==================
18
+
19
+ * Fix error when parsing a template with lots of HTML attributes.
20
+ * Add `HtmlParsingError` check for reporting errors during HTML parsing.
21
+
22
+ v0.10.1 / 2021-06-11
23
+ ==================
24
+
25
+ * Fix LSP diagnostics not being merged properly when analyzing a single file.
26
+ Causing VSCode problems not being cleared after fixing.
27
+
28
+ v0.10.0 / 2021-06-08
29
+ ==================
30
+
31
+ * Add ImgLazyLoading check for recommending loading="lazy" attribute
32
+
33
+ v0.9.1 / 2021-06-04
34
+ ==================
35
+
36
+ * Convert `RemoteAsset` into an `HtmlCheck`
37
+ * Move Liquid logic from `RemoteAsset` to a new `AssetUrlFilters` check
38
+
2
39
  v0.9.0 / 2021-05-28
3
40
  ==================
4
41
 
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
- Under `lib/theme_check/checks`, create new Ruby file with a unique name describing what you want to check for.
39
+ Run `bundle exec rake "new_check[MyNewCheckName]"` to generate all the files required to create a new check.
40
40
 
41
- ```ruby
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
- Add the new check to `config/default.yml` to enable it. If the check is configurable, the `initialize` argument name and default values should also be duplicated inside `config/default.yml`.
43
+ We done implementing your check, add it to `config/default.yml` to enable it:
126
44
 
127
45
  ```yaml
128
- MyCheckName:
46
+ MyNewCheckName:
129
47
  enabled: true
48
+ ignore: []
130
49
  ```
131
50
 
132
- Add a corresponding test file under `test/checks`.
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
- When done, run the tests with `dev test`.
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
- [doctemplate]: /docs/checks/CHECK_DOCS_TEMPLATE.md
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_source}"
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/config/default.yml CHANGED
@@ -12,7 +12,7 @@ ConvertIncludeToRender:
12
12
  LiquidTag:
13
13
  enabled: true
14
14
  ignore: []
15
- min_consecutive_statements: 4
15
+ min_consecutive_statements: 5
16
16
 
17
17
  MissingTemplate:
18
18
  enabled: true
@@ -38,9 +38,13 @@ SyntaxError:
38
38
  TemplateLength:
39
39
  enabled: true
40
40
  ignore: []
41
- max_length: 200
41
+ max_length: 500
42
42
  # Exclude content of {% schema %} in line count
43
43
  exclude_schema: true
44
+ # Exclude content of {% stylesheet %} in line count
45
+ exclude_stylesheet: true
46
+ # Exclude content of {% javascript %} in line count
47
+ exclude_javascript: true
44
48
 
45
49
  UnknownFilter:
46
50
  enabled: true
@@ -99,6 +103,14 @@ DeprecatedFilter:
99
103
  enabled: true
100
104
  ignore: []
101
105
 
106
+ DeprecateLazysizes:
107
+ enabled: true
108
+ ignore: []
109
+
110
+ DeprecateBgsizes:
111
+ enabled: true
112
+ ignore: []
113
+
102
114
  MissingEnableComment:
103
115
  enabled: true
104
116
  ignore: []
@@ -109,16 +121,20 @@ ParserBlockingJavaScript:
109
121
 
110
122
  ParserBlockingScriptTag:
111
123
  enabled: true
124
+ ignore: []
112
125
 
113
126
  AssetSizeJavaScript:
114
127
  enabled: false
115
- ignore: []
116
128
  threshold_in_bytes: 10_000
117
129
  ignore: []
118
130
 
119
131
  AssetSizeCSS:
120
132
  enabled: false
133
+ threshold_in_bytes: 100_000
121
134
  ignore: []
135
+
136
+ AssetSizeCSSStylesheetTag:
137
+ enabled: false
122
138
  threshold_in_bytes: 100_000
123
139
  ignore: []
124
140
 
@@ -130,6 +146,18 @@ RemoteAsset:
130
146
  enabled: true
131
147
  ignore: []
132
148
 
149
+ AssetUrlFilters:
150
+ enabled: true
151
+ ignore: []
152
+
133
153
  ContentForHeaderModification:
134
154
  enabled: true
135
155
  ignore: []
156
+
157
+ ImgLazyLoading:
158
+ enabled: true
159
+ ignore: []
160
+
161
+ HtmlParsingError:
162
+ enabled: true
163
+ ignore: []
@@ -40,6 +40,7 @@
40
40
  - linklist
41
41
  - linklists
42
42
  - location
43
+ - localization
43
44
  - metafield
44
45
  - model
45
46
  - model_source
@@ -55,6 +56,7 @@
55
56
  - powered_by_link
56
57
  - product
57
58
  - product_option
59
+ - product_variant
58
60
  - recommendations
59
61
  - request
60
62
  - routes
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