platformos-check 0.0.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.
Files changed (240) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +2 -0
  3. data/.gitignore +22 -0
  4. data/.rubocop.yml +555 -0
  5. data/CHANGELOG.md +5 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/CONTRIBUTING.md +209 -0
  8. data/Gemfile +33 -0
  9. data/Guardfile +7 -0
  10. data/LICENSE.md +10 -0
  11. data/Makefile +18 -0
  12. data/README.md +189 -0
  13. data/RELEASING.md +35 -0
  14. data/Rakefile +83 -0
  15. data/TROUBLESHOOTING.md +35 -0
  16. data/bin/platformos-check +29 -0
  17. data/bin/platformos-check-language-server +29 -0
  18. data/config/default.yml +98 -0
  19. data/config/nothing.yml +11 -0
  20. data/data/platformos_liquid/built_in_liquid_objects.json +66 -0
  21. data/data/platformos_liquid/deprecated_filters.json +22 -0
  22. data/data/platformos_liquid/documentation/filters.json +6 -0
  23. data/data/platformos_liquid/documentation/latest.json +2 -0
  24. data/data/platformos_liquid/documentation/objects.json +6 -0
  25. data/data/platformos_liquid/documentation/tags.json +6 -0
  26. data/docker/check.Dockerfile +3 -0
  27. data/docker/lsp.Dockerfile +21 -0
  28. data/docs/api/check.md +15 -0
  29. data/docs/api/html_check.md +46 -0
  30. data/docs/api/liquid_check.md +115 -0
  31. data/docs/api/yaml_check.md +19 -0
  32. data/docs/checks/TEMPLATE.md.erb +52 -0
  33. data/docs/checks/convert_include_to_render.md +48 -0
  34. data/docs/checks/deprecated_filter.md +30 -0
  35. data/docs/checks/html_parsing_error.md +50 -0
  36. data/docs/checks/img_lazy_loading.md +63 -0
  37. data/docs/checks/img_width_and_height.md +79 -0
  38. data/docs/checks/invalid_args.md +56 -0
  39. data/docs/checks/liquid_tag.md +65 -0
  40. data/docs/checks/missing_enable_comment.md +50 -0
  41. data/docs/checks/missing_template.md +65 -0
  42. data/docs/checks/parse_json_format.md +76 -0
  43. data/docs/checks/parser_blocking_javascript.md +97 -0
  44. data/docs/checks/required_layout_object.md +28 -0
  45. data/docs/checks/space_inside_braces.md +89 -0
  46. data/docs/checks/syntax_error.md +49 -0
  47. data/docs/checks/template_length.md +45 -0
  48. data/docs/checks/undefined_object.md +71 -0
  49. data/docs/checks/unknown_filter.md +46 -0
  50. data/docs/checks/unused_assign.md +63 -0
  51. data/docs/checks/unused_partial.md +32 -0
  52. data/docs/checks/valid_yaml.md +48 -0
  53. data/docs/flamegraph.svg +18488 -0
  54. data/docs/language_server/code-action-command-palette.png +0 -0
  55. data/docs/language_server/code-action-flow.png +0 -0
  56. data/docs/language_server/code-action-keyboard.png +0 -0
  57. data/docs/language_server/code-action-light-bulb.png +0 -0
  58. data/docs/language_server/code-action-problem.png +0 -0
  59. data/docs/language_server/code-action-quickfix.png +0 -0
  60. data/docs/language_server/how_to_correct_code_with_code_actions_and_execute_command.md +197 -0
  61. data/docs/preview.png +0 -0
  62. data/exe/platformos-check +6 -0
  63. data/exe/platformos-check-language-server +7 -0
  64. data/lib/platformos_check/analyzer.rb +178 -0
  65. data/lib/platformos_check/api_call_file.rb +9 -0
  66. data/lib/platformos_check/app.rb +138 -0
  67. data/lib/platformos_check/app_file.rb +89 -0
  68. data/lib/platformos_check/app_file_rewriter.rb +79 -0
  69. data/lib/platformos_check/asset_file.rb +34 -0
  70. data/lib/platformos_check/bug.rb +23 -0
  71. data/lib/platformos_check/check.rb +163 -0
  72. data/lib/platformos_check/checks/TEMPLATE.rb.erb +11 -0
  73. data/lib/platformos_check/checks/convert_include_to_render.rb +17 -0
  74. data/lib/platformos_check/checks/deprecated_filter.rb +123 -0
  75. data/lib/platformos_check/checks/html_parsing_error.rb +13 -0
  76. data/lib/platformos_check/checks/img_lazy_loading.rb +18 -0
  77. data/lib/platformos_check/checks/img_width_and_height.rb +46 -0
  78. data/lib/platformos_check/checks/invalid_args.rb +81 -0
  79. data/lib/platformos_check/checks/liquid_tag.rb +47 -0
  80. data/lib/platformos_check/checks/missing_enable_comment.rb +37 -0
  81. data/lib/platformos_check/checks/missing_template.rb +107 -0
  82. data/lib/platformos_check/checks/parse_json_format.rb +31 -0
  83. data/lib/platformos_check/checks/parser_blocking_javascript.rb +17 -0
  84. data/lib/platformos_check/checks/required_layout_object.rb +41 -0
  85. data/lib/platformos_check/checks/space_inside_braces.rb +150 -0
  86. data/lib/platformos_check/checks/syntax_error.rb +31 -0
  87. data/lib/platformos_check/checks/template_length.rb +20 -0
  88. data/lib/platformos_check/checks/undefined_object.rb +206 -0
  89. data/lib/platformos_check/checks/unknown_filter.rb +27 -0
  90. data/lib/platformos_check/checks/unused_assign.rb +101 -0
  91. data/lib/platformos_check/checks/unused_partial.rb +93 -0
  92. data/lib/platformos_check/checks/valid_yaml.rb +16 -0
  93. data/lib/platformos_check/checks.rb +73 -0
  94. data/lib/platformos_check/checks_tracking.rb +9 -0
  95. data/lib/platformos_check/cli.rb +239 -0
  96. data/lib/platformos_check/config.rb +219 -0
  97. data/lib/platformos_check/config_file.rb +6 -0
  98. data/lib/platformos_check/corrector.rb +68 -0
  99. data/lib/platformos_check/disabled_check.rb +44 -0
  100. data/lib/platformos_check/disabled_checks.rb +96 -0
  101. data/lib/platformos_check/email_file.rb +9 -0
  102. data/lib/platformos_check/exceptions.rb +36 -0
  103. data/lib/platformos_check/file_system_storage.rb +93 -0
  104. data/lib/platformos_check/graphql_file.rb +68 -0
  105. data/lib/platformos_check/html_check.rb +8 -0
  106. data/lib/platformos_check/html_node.rb +210 -0
  107. data/lib/platformos_check/html_visitor.rb +36 -0
  108. data/lib/platformos_check/in_memory_storage.rb +68 -0
  109. data/lib/platformos_check/json_file.rb +57 -0
  110. data/lib/platformos_check/json_helper.rb +73 -0
  111. data/lib/platformos_check/json_helpers.rb +24 -0
  112. data/lib/platformos_check/json_printer.rb +32 -0
  113. data/lib/platformos_check/language_server/bridge.rb +167 -0
  114. data/lib/platformos_check/language_server/channel.rb +69 -0
  115. data/lib/platformos_check/language_server/client_capabilities.rb +27 -0
  116. data/lib/platformos_check/language_server/code_action_engine.rb +32 -0
  117. data/lib/platformos_check/language_server/code_action_provider.rb +41 -0
  118. data/lib/platformos_check/language_server/code_action_providers/quickfix_code_action_provider.rb +85 -0
  119. data/lib/platformos_check/language_server/code_action_providers/source_fix_all_code_action_provider.rb +41 -0
  120. data/lib/platformos_check/language_server/completion_context.rb +52 -0
  121. data/lib/platformos_check/language_server/completion_engine.rb +32 -0
  122. data/lib/platformos_check/language_server/completion_helper.rb +26 -0
  123. data/lib/platformos_check/language_server/completion_provider.rb +53 -0
  124. data/lib/platformos_check/language_server/completion_providers/assignments_completion_provider.rb +40 -0
  125. data/lib/platformos_check/language_server/completion_providers/filter_completion_provider.rb +102 -0
  126. data/lib/platformos_check/language_server/completion_providers/object_attribute_completion_provider.rb +48 -0
  127. data/lib/platformos_check/language_server/completion_providers/object_completion_provider.rb +38 -0
  128. data/lib/platformos_check/language_server/completion_providers/render_snippet_completion_provider.rb +50 -0
  129. data/lib/platformos_check/language_server/completion_providers/tag_completion_provider.rb +41 -0
  130. data/lib/platformos_check/language_server/configuration.rb +89 -0
  131. data/lib/platformos_check/language_server/constants.rb +29 -0
  132. data/lib/platformos_check/language_server/diagnostic.rb +129 -0
  133. data/lib/platformos_check/language_server/diagnostics_engine.rb +131 -0
  134. data/lib/platformos_check/language_server/diagnostics_manager.rb +184 -0
  135. data/lib/platformos_check/language_server/document_change_corrector.rb +271 -0
  136. data/lib/platformos_check/language_server/document_link_engine.rb +21 -0
  137. data/lib/platformos_check/language_server/document_link_provider.rb +71 -0
  138. data/lib/platformos_check/language_server/document_link_providers/asset_document_link_provider.rb +11 -0
  139. data/lib/platformos_check/language_server/document_link_providers/include_document_link_provider.rb +11 -0
  140. data/lib/platformos_check/language_server/document_link_providers/render_document_link_provider.rb +11 -0
  141. data/lib/platformos_check/language_server/document_link_providers/section_document_link_provider.rb +11 -0
  142. data/lib/platformos_check/language_server/execute_command_engine.rb +19 -0
  143. data/lib/platformos_check/language_server/execute_command_provider.rb +30 -0
  144. data/lib/platformos_check/language_server/execute_command_providers/correction_execute_command_provider.rb +48 -0
  145. data/lib/platformos_check/language_server/execute_command_providers/run_checks_execute_command_provider.rb +28 -0
  146. data/lib/platformos_check/language_server/handler.rb +310 -0
  147. data/lib/platformos_check/language_server/hover_engine.rb +32 -0
  148. data/lib/platformos_check/language_server/hover_provider.rb +53 -0
  149. data/lib/platformos_check/language_server/hover_providers/filter_hover_provider.rb +113 -0
  150. data/lib/platformos_check/language_server/io_messenger.rb +109 -0
  151. data/lib/platformos_check/language_server/messenger.rb +27 -0
  152. data/lib/platformos_check/language_server/protocol.rb +55 -0
  153. data/lib/platformos_check/language_server/server.rb +188 -0
  154. data/lib/platformos_check/language_server/tokens.rb +55 -0
  155. data/lib/platformos_check/language_server/type_helper.rb +22 -0
  156. data/lib/platformos_check/language_server/uri_helper.rb +39 -0
  157. data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/node_handler.rb +87 -0
  158. data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope.rb +60 -0
  159. data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope_visitor.rb +44 -0
  160. data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder.rb +76 -0
  161. data/lib/platformos_check/language_server/variable_lookup_finder/constants.rb +44 -0
  162. data/lib/platformos_check/language_server/variable_lookup_finder/liquid_fixer.rb +103 -0
  163. data/lib/platformos_check/language_server/variable_lookup_finder/potential_lookup.rb +10 -0
  164. data/lib/platformos_check/language_server/variable_lookup_finder/tolerant_parser.rb +94 -0
  165. data/lib/platformos_check/language_server/variable_lookup_finder.rb +262 -0
  166. data/lib/platformos_check/language_server/variable_lookup_traverser.rb +70 -0
  167. data/lib/platformos_check/language_server/versioned_in_memory_storage.rb +84 -0
  168. data/lib/platformos_check/language_server.rb +71 -0
  169. data/lib/platformos_check/layout_file.rb +15 -0
  170. data/lib/platformos_check/liquid_check.rb +10 -0
  171. data/lib/platformos_check/liquid_file.rb +102 -0
  172. data/lib/platformos_check/liquid_node.rb +570 -0
  173. data/lib/platformos_check/liquid_visitor.rb +39 -0
  174. data/lib/platformos_check/migration_file.rb +9 -0
  175. data/lib/platformos_check/node.rb +53 -0
  176. data/lib/platformos_check/offense.rb +228 -0
  177. data/lib/platformos_check/page_file.rb +9 -0
  178. data/lib/platformos_check/parsing_helpers.rb +21 -0
  179. data/lib/platformos_check/partial_file.rb +15 -0
  180. data/lib/platformos_check/platformos_liquid/deprecated_filter.rb +31 -0
  181. data/lib/platformos_check/platformos_liquid/documentation/markdown_template.rb +51 -0
  182. data/lib/platformos_check/platformos_liquid/documentation.rb +45 -0
  183. data/lib/platformos_check/platformos_liquid/filter.rb +19 -0
  184. data/lib/platformos_check/platformos_liquid/object.rb +15 -0
  185. data/lib/platformos_check/platformos_liquid/source_index/base_entry.rb +66 -0
  186. data/lib/platformos_check/platformos_liquid/source_index/base_state.rb +23 -0
  187. data/lib/platformos_check/platformos_liquid/source_index/filter_entry.rb +26 -0
  188. data/lib/platformos_check/platformos_liquid/source_index/filter_state.rb +11 -0
  189. data/lib/platformos_check/platformos_liquid/source_index/object_entry.rb +20 -0
  190. data/lib/platformos_check/platformos_liquid/source_index/object_state.rb +11 -0
  191. data/lib/platformos_check/platformos_liquid/source_index/parameter_entry.rb +25 -0
  192. data/lib/platformos_check/platformos_liquid/source_index/property_entry.rb +21 -0
  193. data/lib/platformos_check/platformos_liquid/source_index/return_type_entry.rb +41 -0
  194. data/lib/platformos_check/platformos_liquid/source_index/tag_entry.rb +24 -0
  195. data/lib/platformos_check/platformos_liquid/source_index/tag_state.rb +11 -0
  196. data/lib/platformos_check/platformos_liquid/source_index.rb +79 -0
  197. data/lib/platformos_check/platformos_liquid/source_manager.rb +116 -0
  198. data/lib/platformos_check/platformos_liquid/tag.rb +59 -0
  199. data/lib/platformos_check/platformos_liquid.rb +21 -0
  200. data/lib/platformos_check/position.rb +180 -0
  201. data/lib/platformos_check/position_helper.rb +57 -0
  202. data/lib/platformos_check/printer.rb +87 -0
  203. data/lib/platformos_check/regex_helpers.rb +21 -0
  204. data/lib/platformos_check/releaser.rb +43 -0
  205. data/lib/platformos_check/schema_file.rb +6 -0
  206. data/lib/platformos_check/sms_file.rb +9 -0
  207. data/lib/platformos_check/storage.rb +29 -0
  208. data/lib/platformos_check/string_helpers.rb +48 -0
  209. data/lib/platformos_check/tags/background.rb +67 -0
  210. data/lib/platformos_check/tags/base.rb +14 -0
  211. data/lib/platformos_check/tags/base_block.rb +14 -0
  212. data/lib/platformos_check/tags/base_tag_methods.rb +59 -0
  213. data/lib/platformos_check/tags/cache.rb +13 -0
  214. data/lib/platformos_check/tags/export.rb +30 -0
  215. data/lib/platformos_check/tags/form.rb +19 -0
  216. data/lib/platformos_check/tags/function.rb +58 -0
  217. data/lib/platformos_check/tags/graphql.rb +70 -0
  218. data/lib/platformos_check/tags/hash_assign.rb +75 -0
  219. data/lib/platformos_check/tags/log.rb +15 -0
  220. data/lib/platformos_check/tags/parse_json.rb +24 -0
  221. data/lib/platformos_check/tags/print.rb +20 -0
  222. data/lib/platformos_check/tags/redirect_to.rb +15 -0
  223. data/lib/platformos_check/tags/render.rb +60 -0
  224. data/lib/platformos_check/tags/response_headers.rb +20 -0
  225. data/lib/platformos_check/tags/response_status.rb +20 -0
  226. data/lib/platformos_check/tags/return.rb +20 -0
  227. data/lib/platformos_check/tags/session.rb +27 -0
  228. data/lib/platformos_check/tags/sign_in.rb +27 -0
  229. data/lib/platformos_check/tags/spam_protection.rb +15 -0
  230. data/lib/platformos_check/tags/theme_render.rb +58 -0
  231. data/lib/platformos_check/tags/try.rb +59 -0
  232. data/lib/platformos_check/tags.rb +65 -0
  233. data/lib/platformos_check/translation_file.rb +6 -0
  234. data/lib/platformos_check/user_schema_file.rb +6 -0
  235. data/lib/platformos_check/version.rb +5 -0
  236. data/lib/platformos_check/yaml_check.rb +11 -0
  237. data/lib/platformos_check/yaml_file.rb +57 -0
  238. data/lib/platformos_check.rb +106 -0
  239. data/platformos-check.gemspec +34 -0
  240. metadata +329 -0
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parser'
4
+
5
+ module PlatformosCheck
6
+ class AppFileRewriter
7
+ def initialize(name, source)
8
+ @buffer = Parser::Source::Buffer.new(name, source:)
9
+ @rewriter = Parser::Source::TreeRewriter.new(
10
+ @buffer
11
+ )
12
+ end
13
+
14
+ def insert_before(node, content, character_range = nil)
15
+ @rewriter.insert_before(
16
+ range(
17
+ character_range&.begin || node.start_index,
18
+ character_range&.end || node.end_index
19
+ ),
20
+ content
21
+ )
22
+ end
23
+
24
+ def insert_after(node, content, character_range = nil)
25
+ @rewriter.insert_after(
26
+ range(
27
+ character_range&.begin || node.start_index,
28
+ character_range&.end || node.end_index
29
+ ),
30
+ content
31
+ )
32
+ end
33
+
34
+ def remove(node)
35
+ @rewriter.remove(
36
+ range(node.outer_markup_start_index, node.outer_markup_end_index)
37
+ )
38
+ end
39
+
40
+ def replace(node, content, character_range = nil)
41
+ @rewriter.replace(
42
+ range(
43
+ character_range&.begin || node.start_index,
44
+ character_range&.end || node.end_index
45
+ ),
46
+ content
47
+ )
48
+ end
49
+
50
+ def replace_inner_markup(node, content)
51
+ @rewriter.replace(
52
+ range(node.inner_markup_start_index, node.inner_markup_end_index),
53
+ content
54
+ )
55
+ end
56
+
57
+ def wrap(node, insert_before, insert_after)
58
+ @rewriter.wrap(
59
+ range(node.start_index, node.end_index),
60
+ insert_before,
61
+ insert_after
62
+ )
63
+ end
64
+
65
+ def to_s
66
+ @rewriter.process
67
+ end
68
+
69
+ private
70
+
71
+ def range(start_index, end_index)
72
+ Parser::Source::Range.new(
73
+ @buffer,
74
+ start_index,
75
+ end_index
76
+ )
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+
5
+ module PlatformosCheck
6
+ class AssetFile < AppFile
7
+ def initialize(relative_path, storage)
8
+ super
9
+ @loaded = false
10
+ @content = nil
11
+ end
12
+
13
+ def rewriter
14
+ @rewriter ||= AppFileRewriter.new(@relative_path, source)
15
+ end
16
+
17
+ def write
18
+ content = rewriter.to_s
19
+ return unless source != content
20
+
21
+ @storage.write(@relative_path, content.gsub("\n", @eol))
22
+ @source = content
23
+ @rewriter = nil
24
+ end
25
+
26
+ def gzipped_size
27
+ @gzipped_size ||= Zlib.gzip(source).bytesize
28
+ end
29
+
30
+ def name
31
+ relative_path.to_s
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'platformos_check/version'
4
+
5
+ module PlatformosCheck
6
+ class PlatformosCheckError < StandardError; end
7
+
8
+ BUG_POSTAMBLE = <<~EOS
9
+ PlatformOS Check Version: #{VERSION}
10
+ Ruby Version: #{RUBY_VERSION}
11
+ Platform: #{RUBY_PLATFORM}
12
+ Muffin mode: activated
13
+
14
+ ------------------------
15
+ Whoops! It looks like you found a bug in PlatformOS Check.
16
+ Please report it at https://github.com/Platform-OS/platformos-lsp/issues, and include the message above.
17
+ Or cross your fingers real hard, and try again.
18
+ EOS
19
+
20
+ def self.bug(message)
21
+ raise PlatformosCheckError, message + BUG_POSTAMBLE
22
+ end
23
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "json_helpers"
4
+
5
+ module PlatformosCheck
6
+ class Check
7
+ include JsonHelpers
8
+
9
+ attr_accessor :platformos_app, :options
10
+ attr_writer :ignored_patterns, :offenses
11
+
12
+ # The order matters.
13
+ SEVERITIES = %i[
14
+ error
15
+ suggestion
16
+ style
17
+ ]
18
+
19
+ # [severity: sym] => number
20
+ SEVERITY_VALUES = SEVERITIES
21
+ .map
22
+ .with_index { |sev, i| [sev, i] }
23
+ .to_h
24
+
25
+ CATEGORIES = %i[
26
+ liquid
27
+ translation
28
+ html
29
+ yaml
30
+ performance
31
+ graphql
32
+ ]
33
+
34
+ class << self
35
+ def all
36
+ @all ||= []
37
+ end
38
+
39
+ def severity(severity = nil)
40
+ if severity
41
+ raise ArgumentError, "unknown severity. Use: #{SEVERITIES.join(', ')}" unless SEVERITIES.include?(severity)
42
+
43
+ @severity = severity
44
+ end
45
+ @severity if defined?(@severity)
46
+ end
47
+
48
+ def severity_value(severity)
49
+ SEVERITY_VALUES[severity] || -1
50
+ end
51
+
52
+ def categories(*categories)
53
+ @categories ||= []
54
+ if categories.any?
55
+ unknown_categories = categories.select { |category| !CATEGORIES.include?(category) }
56
+ if unknown_categories.any?
57
+ raise ArgumentError,
58
+ "unknown categories: #{unknown_categories.join(', ')}. Use: #{CATEGORIES.join(', ')}"
59
+ end
60
+ @categories = categories
61
+ end
62
+ @categories
63
+ end
64
+ alias category categories
65
+
66
+ def doc(doc = nil)
67
+ @doc = doc if doc
68
+ @doc if defined?(@doc)
69
+ end
70
+
71
+ def docs_url(path)
72
+ "https://github.com/Platform-OS/platformos-lsp/blob/master/docs/checks/#{File.basename(path, '.rb')}.md"
73
+ end
74
+
75
+ def can_disable(disableable = nil)
76
+ @can_disable = disableable unless disableable.nil?
77
+ defined?(@can_disable) ? @can_disable : true
78
+ end
79
+
80
+ def single_file(single_file = nil)
81
+ @single_file = single_file unless single_file.nil?
82
+ defined?(@single_file) ? @single_file : !method_defined?(:on_end)
83
+ end
84
+ end
85
+
86
+ def offenses
87
+ @offenses ||= []
88
+ end
89
+
90
+ def add_offense(message, node: nil, app_file: node&.app_file, markup: nil, line_number: nil, node_markup_offset: 0, &block)
91
+ offenses << Offense.new(check: self, message:, app_file:, node:, markup:, line_number:, node_markup_offset:, correction: block)
92
+ end
93
+
94
+ def severity
95
+ @severity ||= self.class.severity
96
+ end
97
+
98
+ def severity=(severity)
99
+ raise ArgumentError, "unknown severity. Use: #{SEVERITIES.join(', ')}" unless SEVERITIES.include?(severity)
100
+
101
+ @severity = severity
102
+ end
103
+
104
+ def severity_value
105
+ SEVERITY_VALUES[severity]
106
+ end
107
+
108
+ def categories
109
+ self.class.categories
110
+ end
111
+
112
+ def doc
113
+ self.class.doc
114
+ end
115
+
116
+ def code_name
117
+ StringHelpers.demodulize(self.class.name)
118
+ end
119
+
120
+ def ignore!
121
+ @ignored = true
122
+ end
123
+
124
+ def ignored?
125
+ defined?(@ignored) && @ignored
126
+ end
127
+
128
+ def ignored_patterns
129
+ @ignored_patterns ||= []
130
+ end
131
+
132
+ def can_disable?
133
+ self.class.can_disable
134
+ end
135
+
136
+ def single_file?
137
+ self.class.single_file
138
+ end
139
+
140
+ def whole_platformos_app?
141
+ !single_file?
142
+ end
143
+
144
+ def ==(other)
145
+ other.is_a?(Check) && code_name == other.code_name
146
+ end
147
+ alias eql? ==
148
+
149
+ def to_s
150
+ s = +"#{code_name}:\n"
151
+ properties = {
152
+ severity:,
153
+ categories:,
154
+ doc:,
155
+ ignored_patterns:
156
+ }.merge(options)
157
+ properties.each_pair do |name, value|
158
+ s << " #{name}: #{value}\n" if value
159
+ end
160
+ s
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ module PlatformosCheck
3
+ # TODO: inherit from HtmlCheck or YamlCheck 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
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ # Recommends replacing `include` for `render`
5
+ class ConvertIncludeToRender < LiquidCheck
6
+ severity :suggestion
7
+ category :liquid
8
+ doc docs_url(__FILE__)
9
+
10
+ def on_include(node)
11
+ add_offense("`include` is deprecated - convert it to `render`", node:) do |corrector|
12
+ # We need to fix #445 and pass the variables from the context or don't replace at all.
13
+ # corrector.replace(node, "render \'#{node.value.template_name_expr}\' ")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ class DeprecatedFilter < LiquidCheck
5
+ doc docs_url(__FILE__)
6
+ category :liquid
7
+ severity :suggestion
8
+
9
+ # The image_url filter does not accept width or height values
10
+ # greater than this numbr.
11
+ MAX_SIZE = 5760
12
+ SIZE_REGEX = /^\d*x\d*$/
13
+ NAMED_SIZES = {
14
+ "pico" => 16,
15
+ "icon" => 32,
16
+ "thumb" => 50,
17
+ "small" => 100,
18
+ "compact" => 160,
19
+ "medium" => 240,
20
+ "large" => 480,
21
+ "grande" => 600,
22
+ "original" => 1024,
23
+ "master" => nil
24
+ }
25
+
26
+ def on_variable(node)
27
+ used_filters = node.filters.map { |name, *_rest| name }
28
+ used_filters.each do |filter|
29
+ alternatives = PlatformosLiquid::DeprecatedFilter.alternatives(filter)
30
+ next unless alternatives
31
+
32
+ case filter
33
+ when 'img_url'
34
+ add_img_url_offense(node)
35
+ else
36
+ add_default_offense(node, filter, alternatives)
37
+ end
38
+ end
39
+ end
40
+
41
+ def add_default_offense(node, filter, alternatives)
42
+ alternatives = alternatives.map { |alt| "`#{alt}`" }
43
+ add_offense(
44
+ "Deprecated filter `#{filter}`, consider using an alternative: #{alternatives.join(', ')}",
45
+ node:
46
+ )
47
+ end
48
+
49
+ def add_img_url_offense(node)
50
+ img_url_filter = node.filters.find { |filter| filter[0] == "img_url" }
51
+ _name, img_url_filter_size, img_url_filter_props = img_url_filter
52
+ size_spec = img_url_filter_size&.dig(0)
53
+ scale = img_url_filter_props&.delete("scale")
54
+
55
+ # Can't correct those.
56
+ return add_default_offense(node, 'img_url', ['image_url']) unless
57
+ (size_spec.nil? || size_spec.is_a?(String)) &&
58
+ (scale.nil? || scale.is_a?(Numeric))
59
+
60
+ return add_default_offense(node, 'img_url', ['image_url']) if
61
+ size_spec.is_a?(String) &&
62
+ size_spec !~ SIZE_REGEX &&
63
+ !NAMED_SIZES.key?(size_spec)
64
+
65
+ node_source = node.markup
66
+
67
+ node_start_index = node.start_index
68
+ match = node_source.match(/img_url[^|]*/)
69
+ img_url_character_range =
70
+ (node_start_index + match.begin(0))...(node_start_index + match.end(0))
71
+
72
+ scale = (scale || 1).to_i
73
+ width, height = if size_spec.nil?
74
+ [100, 100]
75
+ elsif NAMED_SIZES.key?(size_spec)
76
+ [NAMED_SIZES[size_spec], NAMED_SIZES[size_spec]]
77
+ else
78
+ size_spec.split('x')
79
+ end.map { |v| v.to_i * scale }
80
+
81
+ image_url_filter_params = [
82
+ width && width > 0 ? "width: #{[width, MAX_SIZE].min}" : nil,
83
+ height && height > 0 ? "height: #{[height, MAX_SIZE].min}" : nil
84
+ ]
85
+ image_url_filter_params += (img_url_filter_props || {})
86
+ .map do |k, v|
87
+ case v
88
+ when Liquid::VariableLookup
89
+ "#{k}: #{v.name}"
90
+ when String
91
+ "#{k}: '#{v}'"
92
+ else
93
+ "#{k}: #{v}"
94
+ end
95
+ end
96
+ image_url_filter_params = image_url_filter_params
97
+ .reject(&:nil?)
98
+ .join(", ")
99
+
100
+ trailing_whitespace = match[0].match(/\s*\Z/)[0]
101
+
102
+ image_url_filter = "image_url"
103
+ image_url_filter += ": " + image_url_filter_params unless image_url_filter_params.empty?
104
+ image_url_filter += trailing_whitespace
105
+
106
+ add_offense(
107
+ "Deprecated filter `img_url`, consider using `image_url`",
108
+ node:,
109
+ markup: match[0]
110
+ ) do |corrector|
111
+ corrector.replace(
112
+ node,
113
+ image_url_filter,
114
+ img_url_character_range
115
+ )
116
+ end
117
+
118
+ # If anything goes wrong, fail gracefully by returning the default offense.
119
+ rescue StandardError
120
+ add_default_offense(node, 'img_url', ['image_url'])
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ class HtmlParsingError < HtmlCheck
5
+ severity :error
6
+ category :html
7
+ doc docs_url(__FILE__)
8
+
9
+ def on_parse_error(exception, app_file)
10
+ add_offense("HTML in this template can not be parsed: #{exception.message}", app_file:)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ class ImgLazyLoading < HtmlCheck
5
+ severity :suggestion
6
+ categories :html, :performance
7
+ doc docs_url(__FILE__)
8
+
9
+ ACCEPTED_LOADING_VALUES = %w[lazy eager]
10
+
11
+ def on_img(node)
12
+ loading = node.attributes["loading"]&.downcase
13
+ return if ACCEPTED_LOADING_VALUES.include?(loading)
14
+
15
+ add_offense("Use loading=\"eager\" for images visible in the viewport on load and loading=\"lazy\" for others", node:)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ # Reports errors when trying to use parser-blocking script tags
5
+ class ImgWidthAndHeight < HtmlCheck
6
+ severity :error
7
+ categories :html, :performance
8
+ doc docs_url(__FILE__)
9
+
10
+ ENDS_IN_CSS_UNIT = /(cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|%)$/i
11
+
12
+ def on_img(node)
13
+ width = node.attributes["width"]
14
+ height = node.attributes["height"]
15
+
16
+ record_units_in_field_offenses("width", width, node:)
17
+ record_units_in_field_offenses("height", height, node:)
18
+
19
+ return if node.attributes["src"].nil? || (width && height)
20
+
21
+ missing_width = width.nil?
22
+ missing_height = height.nil?
23
+ error_message = if missing_width && missing_height
24
+ "Missing width and height attributes"
25
+ elsif missing_width
26
+ "Missing width attribute"
27
+ elsif missing_height
28
+ "Missing height attribute"
29
+ end
30
+
31
+ add_offense(error_message, node:)
32
+ end
33
+
34
+ private
35
+
36
+ def record_units_in_field_offenses(attribute, value, node:)
37
+ return unless ENDS_IN_CSS_UNIT.match?(value)
38
+
39
+ value_without_units = value.gsub(ENDS_IN_CSS_UNIT, '')
40
+ add_offense(
41
+ "The #{attribute} attribute does not take units. Replace with \"#{value_without_units}\"",
42
+ node:
43
+ )
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ class InvalidArgs < LiquidCheck
5
+ class ParsedGraphQL
6
+ def initialize(ast)
7
+ @ast = ast
8
+ end
9
+
10
+ def required_arguments
11
+ variables.each_with_object([]) do |v, vars|
12
+ vars << v.name if v.type.is_a?(GraphQL::Language::Nodes::NonNullType)
13
+ end
14
+ end
15
+
16
+ def defined_arguments
17
+ variables.map(&:name)
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :ast
23
+
24
+ def variables
25
+ @variables ||= definition&.variables || []
26
+ end
27
+
28
+ def definition
29
+ @definition ||= ast.definitions.detect { |d| d.is_a?(GraphQL::Language::Nodes::OperationDefinition) }
30
+ end
31
+ end
32
+ severity :error
33
+ category :liquid, :graphql
34
+ doc docs_url(__FILE__)
35
+
36
+ def on_render(node)
37
+ add_duplicated_key_offense(node)
38
+ end
39
+
40
+ def on_function(node)
41
+ add_duplicated_key_offense(node)
42
+ end
43
+
44
+ def on_graphql(node)
45
+ add_duplicated_key_offense(node)
46
+
47
+ return if node.value.inline_query
48
+
49
+ graphql_partial = node.value.partial_name
50
+ return unless graphql_partial.is_a?(String)
51
+
52
+ graqphql_file = platformos_app.grouped_files[GraphqlFile][graphql_partial]
53
+ return unless graqphql_file
54
+
55
+ provided_arguments = node.value.attributes
56
+
57
+ return if provided_arguments.include?('args')
58
+
59
+ parsed_graphql = ParsedGraphQL.new(graqphql_file.parse)
60
+
61
+ required_arguments = parsed_graphql.required_arguments
62
+ defined_arguments = parsed_graphql.defined_arguments
63
+
64
+ (provided_arguments - defined_arguments).each do |name|
65
+ add_offense("Undefined argument `#{name}` provided to `#{graqphql_file.relative_path}`", node:)
66
+ end
67
+
68
+ (required_arguments - provided_arguments).each do |name|
69
+ add_offense("Required argument `#{name}` not provided to `#{graqphql_file.relative_path}`", node:)
70
+ end
71
+ rescue GraphQL::ParseError => e
72
+ add_offense("GraphQL Parse error triggered by `#{graqphql_file.relative_path}`: #{e.message}", node:)
73
+ end
74
+
75
+ def add_duplicated_key_offense(node)
76
+ node.value.duplicated_attrs.each do |duplicated_arg|
77
+ add_offense("Duplicated argument `#{duplicated_arg}`", node:)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ # Recommends using {% liquid ... %} if 5 or more consecutive {% ... %} are found.
5
+ class LiquidTag < LiquidCheck
6
+ severity :suggestion
7
+ category :liquid
8
+ doc docs_url(__FILE__)
9
+
10
+ def initialize(min_consecutive_statements: 5)
11
+ @first_statement = nil
12
+ @consecutive_statements = 0
13
+ @min_consecutive_statements = min_consecutive_statements
14
+ end
15
+
16
+ def on_tag(node)
17
+ if node.inside_liquid_tag?
18
+ reset_consecutive_statements
19
+ # Ignore comments
20
+ elsif !node.comment?
21
+ increment_consecutive_statements(node)
22
+ end
23
+ end
24
+
25
+ def on_string(node)
26
+ # Only reset the counter on outputted strings, and ignore empty line-breaks
27
+ return unless node.parent.block? && !node.value.strip.empty?
28
+
29
+ reset_consecutive_statements
30
+ end
31
+
32
+ def after_document(_node)
33
+ reset_consecutive_statements
34
+ end
35
+
36
+ def increment_consecutive_statements(node)
37
+ @first_statement ||= node
38
+ @consecutive_statements += 1
39
+ end
40
+
41
+ def reset_consecutive_statements
42
+ add_offense("Use {% liquid ... %} to write multiple tags", node: @first_statement) if @consecutive_statements >= @min_consecutive_statements
43
+ @first_statement = nil
44
+ @consecutive_statements = 0
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ class MissingEnableComment < LiquidCheck
5
+ severity :error
6
+ doc docs_url(__FILE__)
7
+
8
+ # Don't allow this check to be disabled with a comment,
9
+ # as we need to be able to check for disabled checks.
10
+ can_disable false
11
+
12
+ def on_document(_node)
13
+ @disabled_checks = DisabledChecks.new
14
+ end
15
+
16
+ def on_comment(node)
17
+ @disabled_checks.update(node)
18
+ end
19
+
20
+ def on_inline_comment(node)
21
+ @disabled_checks.update(node)
22
+ end
23
+
24
+ def after_document(node)
25
+ checks_missing_end_index = @disabled_checks.checks_missing_end_index
26
+ return if checks_missing_end_index.empty?
27
+
28
+ message = if checks_missing_end_index.any? { |name| name == :all }
29
+ "All checks were"
30
+ else
31
+ checks_missing_end_index.join(', ') + " " + (checks_missing_end_index.size == 1 ? "was" : "were")
32
+ end
33
+
34
+ add_offense("#{message} disabled but not re-enabled with platformos-check-enable", node:)
35
+ end
36
+ end
37
+ end