theme-check 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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