theme-check 0.3.2 → 0.7.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/theme-check.yml +10 -3
  3. data/.rubocop.yml +12 -3
  4. data/CHANGELOG.md +42 -0
  5. data/CONTRIBUTING.md +5 -2
  6. data/Gemfile +5 -3
  7. data/LICENSE.md +2 -0
  8. data/README.md +12 -4
  9. data/RELEASING.md +10 -3
  10. data/Rakefile +6 -0
  11. data/config/default.yml +16 -0
  12. data/data/shopify_liquid/tags.yml +27 -0
  13. data/data/shopify_translation_keys.yml +850 -0
  14. data/docs/checks/CHECK_DOCS_TEMPLATE.md +47 -0
  15. data/docs/checks/asset_size_css.md +52 -0
  16. data/docs/checks/asset_size_javascript.md +79 -0
  17. data/docs/checks/convert_include_to_render.md +48 -0
  18. data/docs/checks/default_locale.md +46 -0
  19. data/docs/checks/deprecated_filter.md +46 -0
  20. data/docs/checks/img_width_and_height.md +79 -0
  21. data/docs/checks/liquid_tag.md +65 -0
  22. data/docs/checks/matching_schema_translations.md +93 -0
  23. data/docs/checks/matching_translations.md +72 -0
  24. data/docs/checks/missing_enable_comment.md +50 -0
  25. data/docs/checks/missing_required_template_files.md +26 -0
  26. data/docs/checks/missing_template.md +40 -0
  27. data/docs/checks/nested_snippet.md +69 -0
  28. data/docs/checks/parser_blocking_javascript.md +97 -0
  29. data/docs/checks/remote_asset.md +82 -0
  30. data/docs/checks/required_directories.md +25 -0
  31. data/docs/checks/required_layout_theme_object.md +28 -0
  32. data/docs/checks/space_inside_braces.md +63 -0
  33. data/docs/checks/syntax_error.md +49 -0
  34. data/docs/checks/template_length.md +50 -0
  35. data/docs/checks/translation_key_exists.md +63 -0
  36. data/docs/checks/undefined_object.md +53 -0
  37. data/docs/checks/unknown_filter.md +45 -0
  38. data/docs/checks/unused_assign.md +47 -0
  39. data/docs/checks/unused_snippet.md +32 -0
  40. data/docs/checks/valid_html_translation.md +53 -0
  41. data/docs/checks/valid_json.md +60 -0
  42. data/docs/checks/valid_schema.md +50 -0
  43. data/lib/theme_check.rb +4 -0
  44. data/lib/theme_check/asset_file.rb +34 -0
  45. data/lib/theme_check/check.rb +20 -10
  46. data/lib/theme_check/checks/asset_size_css.rb +89 -0
  47. data/lib/theme_check/checks/asset_size_javascript.rb +68 -0
  48. data/lib/theme_check/checks/convert_include_to_render.rb +1 -1
  49. data/lib/theme_check/checks/default_locale.rb +1 -0
  50. data/lib/theme_check/checks/deprecated_filter.rb +1 -1
  51. data/lib/theme_check/checks/img_width_and_height.rb +74 -0
  52. data/lib/theme_check/checks/liquid_tag.rb +3 -3
  53. data/lib/theme_check/checks/matching_schema_translations.rb +1 -0
  54. data/lib/theme_check/checks/matching_translations.rb +2 -1
  55. data/lib/theme_check/checks/missing_enable_comment.rb +1 -0
  56. data/lib/theme_check/checks/missing_required_template_files.rb +1 -2
  57. data/lib/theme_check/checks/missing_template.rb +1 -0
  58. data/lib/theme_check/checks/nested_snippet.rb +1 -0
  59. data/lib/theme_check/checks/parser_blocking_javascript.rb +8 -15
  60. data/lib/theme_check/checks/remote_asset.rb +98 -0
  61. data/lib/theme_check/checks/required_directories.rb +1 -1
  62. data/lib/theme_check/checks/required_layout_theme_object.rb +1 -1
  63. data/lib/theme_check/checks/space_inside_braces.rb +1 -0
  64. data/lib/theme_check/checks/syntax_error.rb +1 -0
  65. data/lib/theme_check/checks/template_length.rb +1 -0
  66. data/lib/theme_check/checks/translation_key_exists.rb +14 -1
  67. data/lib/theme_check/checks/undefined_object.rb +16 -7
  68. data/lib/theme_check/checks/unknown_filter.rb +1 -0
  69. data/lib/theme_check/checks/unused_assign.rb +5 -3
  70. data/lib/theme_check/checks/unused_snippet.rb +1 -0
  71. data/lib/theme_check/checks/valid_html_translation.rb +2 -1
  72. data/lib/theme_check/checks/valid_json.rb +1 -0
  73. data/lib/theme_check/checks/valid_schema.rb +1 -0
  74. data/lib/theme_check/cli.rb +49 -13
  75. data/lib/theme_check/config.rb +5 -2
  76. data/lib/theme_check/disabled_checks.rb +2 -2
  77. data/lib/theme_check/in_memory_storage.rb +13 -8
  78. data/lib/theme_check/language_server.rb +12 -0
  79. data/lib/theme_check/language_server/completion_engine.rb +38 -0
  80. data/lib/theme_check/language_server/completion_helper.rb +25 -0
  81. data/lib/theme_check/language_server/completion_provider.rb +28 -0
  82. data/lib/theme_check/language_server/completion_providers/filter_completion_provider.rb +51 -0
  83. data/lib/theme_check/language_server/completion_providers/object_completion_provider.rb +31 -0
  84. data/lib/theme_check/language_server/completion_providers/render_snippet_completion_provider.rb +43 -0
  85. data/lib/theme_check/language_server/completion_providers/tag_completion_provider.rb +31 -0
  86. data/lib/theme_check/language_server/constants.rb +10 -0
  87. data/lib/theme_check/language_server/document_link_engine.rb +48 -0
  88. data/lib/theme_check/language_server/handler.rb +105 -10
  89. data/lib/theme_check/language_server/position_helper.rb +27 -0
  90. data/lib/theme_check/language_server/protocol.rb +41 -0
  91. data/lib/theme_check/language_server/server.rb +9 -4
  92. data/lib/theme_check/language_server/tokens.rb +55 -0
  93. data/lib/theme_check/liquid_check.rb +11 -0
  94. data/lib/theme_check/node.rb +1 -2
  95. data/lib/theme_check/offense.rb +52 -15
  96. data/lib/theme_check/regex_helpers.rb +15 -0
  97. data/lib/theme_check/releaser.rb +39 -0
  98. data/lib/theme_check/remote_asset_file.rb +44 -0
  99. data/lib/theme_check/shopify_liquid.rb +1 -0
  100. data/lib/theme_check/shopify_liquid/deprecated_filter.rb +10 -8
  101. data/lib/theme_check/shopify_liquid/filter.rb +3 -5
  102. data/lib/theme_check/shopify_liquid/object.rb +2 -6
  103. data/lib/theme_check/shopify_liquid/tag.rb +14 -0
  104. data/lib/theme_check/storage.rb +3 -3
  105. data/lib/theme_check/string_helpers.rb +47 -0
  106. data/lib/theme_check/tags.rb +1 -2
  107. data/lib/theme_check/theme.rb +7 -1
  108. data/lib/theme_check/version.rb +1 -1
  109. data/theme-check.gemspec +1 -2
  110. metadata +57 -18
@@ -0,0 +1,47 @@
1
+ # Check Title (`CheckClassName`)
2
+
3
+ A brief paragraph explaining why the check exists.
4
+
5
+ ## Check Details
6
+
7
+ This check is aimed at eliminating ...
8
+
9
+ :-1: Examples of **incorrect** code for this check:
10
+
11
+ ```liquid
12
+ ```
13
+
14
+ :+1: Examples of **correct** code for this check:
15
+
16
+ ```liquid
17
+ ```
18
+
19
+ ## Check Options
20
+
21
+ The default configuration for this check is the following:
22
+
23
+ ```yaml
24
+ CheckClassName:
25
+ enabled: true
26
+ some_option: 10
27
+ ```
28
+
29
+ ### `some_option`
30
+
31
+ The `some_option` option (Default: `10`) determines ...
32
+
33
+ ## When Not To Use It
34
+
35
+ If you don't want to ..., then it's safe to disable this rule.
36
+
37
+ ## Version
38
+
39
+ This check has been introduced in Theme Check X.X.X.
40
+
41
+ ## Resources
42
+
43
+ - [Rule Source][codesource]
44
+ - [Documentation Source][docsource]
45
+
46
+ [codesource]: /lib/theme_check/checks/check_class_name.rb
47
+ [docsource]: /docs/checks/check_class_name.md
@@ -0,0 +1,52 @@
1
+ # Prevent Large CSS bundles (`AssetSizeCSS`)
2
+
3
+ This rule exists to prevent large CSS bundles (for speed).
4
+
5
+ ## Check Details
6
+
7
+ This rule disallows the use of too much CSS in themes, as configured by `threshold_in_bytes`.
8
+
9
+ :-1: Examples of **incorrect** code for this check:
10
+ ```liquid
11
+ <!-- Here, assets/theme.css is **greater** than `threshold_in_bytes` compressed. -->
12
+ {{ 'theme.css' | asset_url | stylesheet_tag }}
13
+ ```
14
+
15
+ :+1: Example of **correct** code for this check:
16
+ ```liquid
17
+ <!-- Here, assets/theme.css is **less** than `threshold_in_bytes` compressed. -->
18
+ {{ 'theme.css' | asset_url | stylesheet_tag }}
19
+ ```
20
+
21
+ ## Check Options
22
+
23
+ The default configuration is the following.
24
+
25
+ ```yaml
26
+ AssetSizeCSS:
27
+ enabled: false
28
+ threshold_in_bytes: 100_000
29
+ ```
30
+
31
+ ### `threshold_in_bytes`
32
+
33
+ The `threshold_in_bytes` option (default: `100_000`) determines the maximum allowed compressed size in bytes that a single CSS file can take.
34
+
35
+ This includes theme and remote stylesheets.
36
+
37
+ ## When Not To Use It
38
+
39
+ This rule is safe to disable.
40
+
41
+ ## Version
42
+
43
+ This check has been introduced in Theme Check 0.6.0.
44
+
45
+ ## Resources
46
+
47
+ - [The Performance Inequality Gap](https://infrequently.org/2021/03/the-performance-inequality-gap/)
48
+ - [Rule Source][codesource]
49
+ - [Documentation Source][docsource]
50
+
51
+ [codesource]: /lib/theme_check/checks/asset_size_css.rb
52
+ [docsource]: /docs/checks/asset_size_css.md
@@ -0,0 +1,79 @@
1
+ # Prevent JavaScript Abuse on Server Rendered Themes (`AssetSizeJavaScript`)
2
+
3
+ For server rendered pages, it is an anti-pattern to execute large JavaScript bundles on every navigation.
4
+
5
+ This doesn't mean they don't have a reason to exist. For instance, chat widgets are mini applications embedded inside web pages. Designing such an app with server rendered updates would be absurd. However, if only 10% of the users interact with the chat widget, the other 90% should not have to execute the entire bundle on every page load.
6
+
7
+ The natural solution to this problem is to implement the chat widget using the [Import on Interaction Pattern][ioip].
8
+
9
+ ## Check Details
10
+
11
+ This rule disallows the use of theme JavaScript files and external scripts to have a compressed size greater than a configured `threshold_in_bytes`.
12
+
13
+ :-1: Examples of **incorrect** code for this check:
14
+ ```liquid
15
+ <!-- Here assets/chat-widget.js is more than 10KB gzipped. -->
16
+ <script src="{{ 'chat-widget.js' | asset_url }}" defer></script>
17
+
18
+ <!-- The use of jQuery is discouraged in themes -->
19
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js" defer></script>
20
+ ```
21
+
22
+ :+1: Example of **correct** code for this check:
23
+ ```liquid
24
+ <script>
25
+ const chatWidgetButton = document.getElementById('#chat-widget')
26
+
27
+ chatWidgetButton.addEventListener('click', e => {
28
+ e.preventDefault();
29
+ import("{{ 'chat-widget.js' | asset_url }}")
30
+ .then(module => module.default)
31
+ .then(ChatWidget => ChatWidget.init())
32
+ .catch(err => {
33
+ console.error(err);
34
+ });
35
+ });
36
+ </script>
37
+ ```
38
+
39
+ ## Check Options
40
+
41
+ The default configuration is the following.
42
+
43
+ ```yaml
44
+ AssetSizeJavaScript:
45
+ enabled: false
46
+ threshold_in_bytes: 10000
47
+ ```
48
+
49
+ ### `threshold_in_bytes`
50
+
51
+ The `threshold_in_bytes` option (default: `10000`) determines the maximum allowed compressed size in bytes that a single JavaScript file can take.
52
+
53
+ This includes theme and remote scripts.
54
+
55
+ ## When Not To Use It
56
+
57
+ When you can't do anything about it, it is preferable to disable this rule using the comment syntax:
58
+
59
+ ```
60
+ {% comment %}theme-check-disable AssetSizeJavaScript{% endcomment %}
61
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js" defer></script>
62
+ {% comment %}theme-check-enable AssetSizeJavaScript{% endcomment %}
63
+ ```
64
+
65
+ This makes disabling the rule an explicit affair and shows that the code is smelly.
66
+
67
+ ## Version
68
+
69
+ This check has been introduced in Theme Check 0.5.0.
70
+
71
+ ## Resources
72
+
73
+ - [The Import On Interaction Pattern][ioip]
74
+ - [Rule Source][codesource]
75
+ - [Documentation Source][docsource]
76
+
77
+ [ioip]: https://addyosmani.com/blog/import-on-interaction/
78
+ [codesource]: /lib/theme_check/checks/asset_size_javascript.rb
79
+ [docsource]: /docs/checks/asset_size_javascript.md
@@ -0,0 +1,48 @@
1
+ # Discourage the use of `include` (`ConvertIncludeToRender`)
2
+
3
+ The `include` tag is [deprecated][deprecated]. This tag exists to enforce the use of the `render` tag instead of `include`.
4
+
5
+ The `include` tag works similarly to the `render` tag, but it lets the code inside of the snippet to access and overwrite the variables within its parent template. The `include` tag has been deprecated because the way that it handles variables reduces performance and makes theme code harder to both read and maintain.
6
+
7
+ ## Check Details
8
+
9
+ This check is aimed at eliminating the use of `include` tags.
10
+
11
+ :-1: Examples of **incorrect** code for this check:
12
+
13
+ ```liquid
14
+ {% include 'snippet' %}
15
+ ```
16
+
17
+ :+1: Examples of **correct** code for this check:
18
+
19
+ ```liquid
20
+ {% render 'snippet' %}
21
+ ```
22
+
23
+ ## Check Options
24
+
25
+ The default configuration for this check is the following:
26
+
27
+ ```yaml
28
+ ConvertIncludeToRender:
29
+ enabled: true
30
+ ```
31
+
32
+ ## When Not To Use It
33
+
34
+ It is discouraged to disable this rule.
35
+
36
+ ## Version
37
+
38
+ This check has been introduced in Theme Check 0.1.0.
39
+
40
+ ## Resources
41
+
42
+ - [Deprecated Tags Reference][deprecated]
43
+ - [Rule Source][codesource]
44
+ - [Documentation Source][docsource]
45
+
46
+ [deprecated]: https://shopify.dev/docs/themes/liquid/reference/tags/deprecated-tags#include
47
+ [codesource]: /lib/theme_check/checks/convert_include_to_render.rb
48
+ [docsource]: /docs/checks/convert_include_to_render.md
@@ -0,0 +1,46 @@
1
+ # Ensure theme has a default locale (`DefaultLocale`)
2
+
3
+ This check makes sure the theme has a default translation file.
4
+
5
+ ## Check Details
6
+
7
+ This check makes sure a theme has a default locale.
8
+
9
+ :-1: Example of **incorrect** theme for this check:
10
+
11
+ ```
12
+ locales/
13
+ ├── en.json
14
+ ├── fr.json
15
+ └── zh-TW.json
16
+ ```
17
+
18
+ :+1: Example of **correct** theme for this check:
19
+
20
+ ```
21
+ locales/
22
+ ├── en.default.json # a default translation file is required
23
+ ├── fr.json
24
+ └── zh-TW.json
25
+ ```
26
+
27
+ ## Check Options
28
+
29
+ The default configuration for this check is the following:
30
+
31
+ ```yaml
32
+ DefaultLocale:
33
+ enabled: true
34
+ ```
35
+
36
+ ## Version
37
+
38
+ This check has been introduced in Theme Check 0.1.0.
39
+
40
+ ## Resources
41
+
42
+ - [Rule Source][codesource]
43
+ - [Documentation Source][docsource]
44
+
45
+ [codesource]: /lib/theme_check/checks/default_locale.rb
46
+ [docsource]: /docs/checks/default_locale.md
@@ -0,0 +1,46 @@
1
+ # Discourage use of deprecated filters (`DeprecatedFilter`)
2
+
3
+ This check discourages the use of [deprecated filters][deprecated].
4
+
5
+ ## Check Details
6
+
7
+ This check is aimed at eliminating deprecated filters.
8
+
9
+ :-1: Example of **incorrect** code for this check:
10
+
11
+ ```liquid
12
+ .site-footer p {
13
+ color: {{ settings.color_name | hex_to_rgba: 0.5 }};
14
+ }
15
+ ```
16
+
17
+ :+1: Example of **correct** code for this check:
18
+
19
+ ```liquid
20
+ .site-footer p {
21
+ color: {{ settings.color_name | color_modify: 'alpha', 0.5 }};
22
+ }
23
+ ```
24
+
25
+ ## Check Options
26
+
27
+ The default configuration for this check is the following:
28
+
29
+ ```yaml
30
+ DeprecatedFilter:
31
+ enabled: true
32
+ ```
33
+
34
+ ## Version
35
+
36
+ This check has been introduced in Theme Check 0.2.0.
37
+
38
+ ## Resources
39
+
40
+ - [Deprecated Filters Reference][deprecated]
41
+ - [Rule Source][codesource]
42
+ - [Documentation Source][docsource]
43
+
44
+ [deprecated]: https://shopify.dev/docs/themes/liquid/reference/filters/deprecated-filters
45
+ [codesource]: /lib/theme_check/checks/deprecated_filter.rb
46
+ [docsource]: /docs/checks/deprecated_filter.md
@@ -0,0 +1,79 @@
1
+ # Width and height attributes on image tags (`ImgWidthAndHeight`)
2
+
3
+ This check exists to prevent [cumulative layout shift][cls] (CLS) in themes.
4
+
5
+ The absence of `width` and `height` attributes on an `img` tag prevents the browser from knowing the aspect ratio of the image before it is downloaded. Unless another technique is used to allocate space, the browser will consider the image to be of height 0 until it is loaded.
6
+
7
+ This has numerous nefarious implications:
8
+
9
+ 1. [This causes layout shift as images start appearing one after the other.][codepenshift] Text starts flying down the page as the image pushes it down.
10
+ 2. [This breaks lazy loading.][codepenlazy] When all images have a height of 0px, every image is inside the viewport. And when everything is in the viewport, everything gets loaded. There's nothing lazy about it!
11
+
12
+ The fix is easy. Make sure the `width` and `height` attribute are set on the `img` tag and that the CSS width of the image is set.
13
+
14
+ Note: The width and height attributes of an image do not have units.
15
+
16
+ ## Check Details
17
+
18
+ This check is aimed at eliminating content layout shift in themes by enforcing the use of the `width` and `height` attributes on `img` tags.
19
+
20
+ :-1: Examples of **incorrect** code for this check:
21
+
22
+ ```liquid
23
+ <img alt="cat" src="cat.jpg">
24
+ <img alt="cat" src="cat.jpg" width="100px" height="100px">
25
+ <img alt="{{ image.alt }}" src="{{ image.src }}">
26
+ ```
27
+
28
+ :+1: Examples of **correct** code for this check:
29
+
30
+ ```liquid
31
+ <img alt="cat" src="cat.jpg" width="100" height="200">
32
+ <img
33
+ alt="{{ image.alt }}"
34
+ src="{{ image.src }}"
35
+ width="{{ image.width }}"
36
+ height="{{ image.height }}"
37
+ >
38
+ ```
39
+
40
+ **NOTE:** The CSS `width` of the `img` should _also_ be set for the image to be responsive.
41
+
42
+ ## Check Options
43
+
44
+ The default configuration for this check is the following:
45
+
46
+ ```yaml
47
+ ImgWidthAndHeight:
48
+ enabled: true
49
+ ```
50
+
51
+ ## When Not To Use It
52
+
53
+ There are some cases where you can avoid content-layout shift without needing the width and height attributes:
54
+
55
+ - When the aspect-ratio of the displayed image should be independent of the uploaded image. In those cases, the solution is still the padding-top hack with an `overflow: hidden container`.
56
+ - When you are happy with the padding-top hack.
57
+
58
+ In those cases, it is fine to disable this check with the comment.
59
+
60
+ It is otherwise unwise to disable this check, since it would negatively impact the mobile search ranking of the merchants using your theme.
61
+
62
+ ## Version
63
+
64
+ This check has been introduced in Theme Check 0.6.0.
65
+
66
+ ## Resources
67
+
68
+ - [Cumulative Layout Shift Reference][cls]
69
+ - [Codepen illustrating the impact of width and height on layout shift][codepenshift]
70
+ - [Codepen illustrating the impact of width and height on lazy loading][codepenlazy]
71
+ - [Rule Source][codesource]
72
+ - [Documentation Source][docsource]
73
+
74
+ [cls]: https://web.dev/cls/
75
+ [codepenshift]: https://codepen.io/charlespwd/pen/YzpxPEp?editors=1100
76
+ [codepenlazy]: https://codepen.io/charlespwd/pen/abZmqXJ?editors=0111
77
+ [aspect-ratio]: https://caniuse.com/mdn-css_properties_aspect-ratio
78
+ [codesource]: /lib/theme_check/checks/img_aspect_ratio.rb
79
+ [docsource]: /docs/checks/img_aspect_ratio.md
@@ -0,0 +1,65 @@
1
+ # Encourage use of liquid tag for consecutive statements (LiquidTag)
2
+
3
+ Recommends using `{% liquid ... %}` if 4 or more consecutive liquid tags (`{% ... %}`) are found.
4
+
5
+ ## Check Details
6
+
7
+ This check is aimed at eliminating repetitive tag markers (`{%` and `%}`) in theme files.
8
+
9
+ :-1: Example of **incorrect** code for this check:
10
+
11
+ ```liquid
12
+ {% if collection.image.size != 0 %}
13
+ {% assign collection_image = collection.image %}
14
+ {% elsif collection.products.first.size != 0 and collection.products.first.media != empty %}
15
+ {% assign collection_image = collection.products.first.featured_media.preview_image %}
16
+ {% else %}
17
+ {% assign collection_image = nil %}
18
+ {% endif %}
19
+ ```
20
+
21
+ :+1: Example of **correct** code for this check:
22
+
23
+ ```liquid
24
+ {%- liquid
25
+ if collection.image.size != 0
26
+ assign collection_image = collection.image
27
+ elsif collection.products.first.size != 0 and collection.products.first.media != empty
28
+ assign collection_image = collection.products.first.featured_media.preview_image
29
+ else
30
+ assign collection_image = nil
31
+ endif
32
+ -%}
33
+ ```
34
+
35
+ ## Check Options
36
+
37
+ The default configuration for this check is the following:
38
+
39
+ ```yaml
40
+ LiquidTag:
41
+ enabled: true
42
+ min_consecutive_statements: 4
43
+ ```
44
+
45
+ ### `min_consecutive_statements`
46
+
47
+ The `min_consecutive_statements` option (Default: `4`) determines the maximum (inclusive) number of consecutive statements before the check recommends a refactor.
48
+
49
+ ## When Not To Use It
50
+
51
+ It's safe to disable this rule.
52
+
53
+ ## Version
54
+
55
+ This check has been introduced in Theme Check 0.1.0.
56
+
57
+ ## Resources
58
+
59
+ - [`{% liquid %}` Tag Reference][liquid]
60
+ - [Rule Source][codesource]
61
+ - [Documentation Source][docsource]
62
+
63
+ [liquid]: https://shopify.dev/docs/themes/liquid/reference/tags/theme-tags#liquid
64
+ [codesource]: /lib/theme_check/checks/liquid_tag.rb
65
+ [docsource]: /docs/checks/liquid_tag.md