platformos-check 0.0.1

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