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.
- checksums.yaml +7 -0
- data/.dockerignore +2 -0
- data/.gitignore +22 -0
- data/.rubocop.yml +555 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +209 -0
- data/Gemfile +33 -0
- data/Guardfile +7 -0
- data/LICENSE.md +10 -0
- data/Makefile +18 -0
- data/README.md +189 -0
- data/RELEASING.md +35 -0
- data/Rakefile +83 -0
- data/TROUBLESHOOTING.md +35 -0
- data/bin/platformos-check +29 -0
- data/bin/platformos-check-language-server +29 -0
- data/config/default.yml +98 -0
- data/config/nothing.yml +11 -0
- data/data/platformos_liquid/built_in_liquid_objects.json +66 -0
- data/data/platformos_liquid/deprecated_filters.json +22 -0
- data/data/platformos_liquid/documentation/filters.json +6 -0
- data/data/platformos_liquid/documentation/latest.json +2 -0
- data/data/platformos_liquid/documentation/objects.json +6 -0
- data/data/platformos_liquid/documentation/tags.json +6 -0
- data/docker/check.Dockerfile +3 -0
- data/docker/lsp.Dockerfile +21 -0
- data/docs/api/check.md +15 -0
- data/docs/api/html_check.md +46 -0
- data/docs/api/liquid_check.md +115 -0
- data/docs/api/yaml_check.md +19 -0
- data/docs/checks/TEMPLATE.md.erb +52 -0
- data/docs/checks/convert_include_to_render.md +48 -0
- data/docs/checks/deprecated_filter.md +30 -0
- data/docs/checks/html_parsing_error.md +50 -0
- data/docs/checks/img_lazy_loading.md +63 -0
- data/docs/checks/img_width_and_height.md +79 -0
- data/docs/checks/invalid_args.md +56 -0
- data/docs/checks/liquid_tag.md +65 -0
- data/docs/checks/missing_enable_comment.md +50 -0
- data/docs/checks/missing_template.md +65 -0
- data/docs/checks/parse_json_format.md +76 -0
- data/docs/checks/parser_blocking_javascript.md +97 -0
- data/docs/checks/required_layout_object.md +28 -0
- data/docs/checks/space_inside_braces.md +89 -0
- data/docs/checks/syntax_error.md +49 -0
- data/docs/checks/template_length.md +45 -0
- data/docs/checks/undefined_object.md +71 -0
- data/docs/checks/unknown_filter.md +46 -0
- data/docs/checks/unused_assign.md +63 -0
- data/docs/checks/unused_partial.md +32 -0
- data/docs/checks/valid_yaml.md +48 -0
- data/docs/flamegraph.svg +18488 -0
- data/docs/language_server/code-action-command-palette.png +0 -0
- data/docs/language_server/code-action-flow.png +0 -0
- data/docs/language_server/code-action-keyboard.png +0 -0
- data/docs/language_server/code-action-light-bulb.png +0 -0
- data/docs/language_server/code-action-problem.png +0 -0
- data/docs/language_server/code-action-quickfix.png +0 -0
- data/docs/language_server/how_to_correct_code_with_code_actions_and_execute_command.md +197 -0
- data/docs/preview.png +0 -0
- data/exe/platformos-check +6 -0
- data/exe/platformos-check-language-server +7 -0
- data/lib/platformos_check/analyzer.rb +178 -0
- data/lib/platformos_check/api_call_file.rb +9 -0
- data/lib/platformos_check/app.rb +138 -0
- data/lib/platformos_check/app_file.rb +89 -0
- data/lib/platformos_check/app_file_rewriter.rb +79 -0
- data/lib/platformos_check/asset_file.rb +34 -0
- data/lib/platformos_check/bug.rb +23 -0
- data/lib/platformos_check/check.rb +163 -0
- data/lib/platformos_check/checks/TEMPLATE.rb.erb +11 -0
- data/lib/platformos_check/checks/convert_include_to_render.rb +17 -0
- data/lib/platformos_check/checks/deprecated_filter.rb +123 -0
- data/lib/platformos_check/checks/html_parsing_error.rb +13 -0
- data/lib/platformos_check/checks/img_lazy_loading.rb +18 -0
- data/lib/platformos_check/checks/img_width_and_height.rb +46 -0
- data/lib/platformos_check/checks/invalid_args.rb +81 -0
- data/lib/platformos_check/checks/liquid_tag.rb +47 -0
- data/lib/platformos_check/checks/missing_enable_comment.rb +37 -0
- data/lib/platformos_check/checks/missing_template.rb +107 -0
- data/lib/platformos_check/checks/parse_json_format.rb +31 -0
- data/lib/platformos_check/checks/parser_blocking_javascript.rb +17 -0
- data/lib/platformos_check/checks/required_layout_object.rb +41 -0
- data/lib/platformos_check/checks/space_inside_braces.rb +150 -0
- data/lib/platformos_check/checks/syntax_error.rb +31 -0
- data/lib/platformos_check/checks/template_length.rb +20 -0
- data/lib/platformos_check/checks/undefined_object.rb +206 -0
- data/lib/platformos_check/checks/unknown_filter.rb +27 -0
- data/lib/platformos_check/checks/unused_assign.rb +101 -0
- data/lib/platformos_check/checks/unused_partial.rb +93 -0
- data/lib/platformos_check/checks/valid_yaml.rb +16 -0
- data/lib/platformos_check/checks.rb +73 -0
- data/lib/platformos_check/checks_tracking.rb +9 -0
- data/lib/platformos_check/cli.rb +239 -0
- data/lib/platformos_check/config.rb +219 -0
- data/lib/platformos_check/config_file.rb +6 -0
- data/lib/platformos_check/corrector.rb +68 -0
- data/lib/platformos_check/disabled_check.rb +44 -0
- data/lib/platformos_check/disabled_checks.rb +96 -0
- data/lib/platformos_check/email_file.rb +9 -0
- data/lib/platformos_check/exceptions.rb +36 -0
- data/lib/platformos_check/file_system_storage.rb +93 -0
- data/lib/platformos_check/graphql_file.rb +68 -0
- data/lib/platformos_check/html_check.rb +8 -0
- data/lib/platformos_check/html_node.rb +210 -0
- data/lib/platformos_check/html_visitor.rb +36 -0
- data/lib/platformos_check/in_memory_storage.rb +68 -0
- data/lib/platformos_check/json_file.rb +57 -0
- data/lib/platformos_check/json_helper.rb +73 -0
- data/lib/platformos_check/json_helpers.rb +24 -0
- data/lib/platformos_check/json_printer.rb +32 -0
- data/lib/platformos_check/language_server/bridge.rb +167 -0
- data/lib/platformos_check/language_server/channel.rb +69 -0
- data/lib/platformos_check/language_server/client_capabilities.rb +27 -0
- data/lib/platformos_check/language_server/code_action_engine.rb +32 -0
- data/lib/platformos_check/language_server/code_action_provider.rb +41 -0
- data/lib/platformos_check/language_server/code_action_providers/quickfix_code_action_provider.rb +85 -0
- data/lib/platformos_check/language_server/code_action_providers/source_fix_all_code_action_provider.rb +41 -0
- data/lib/platformos_check/language_server/completion_context.rb +52 -0
- data/lib/platformos_check/language_server/completion_engine.rb +32 -0
- data/lib/platformos_check/language_server/completion_helper.rb +26 -0
- data/lib/platformos_check/language_server/completion_provider.rb +53 -0
- data/lib/platformos_check/language_server/completion_providers/assignments_completion_provider.rb +40 -0
- data/lib/platformos_check/language_server/completion_providers/filter_completion_provider.rb +102 -0
- data/lib/platformos_check/language_server/completion_providers/object_attribute_completion_provider.rb +48 -0
- data/lib/platformos_check/language_server/completion_providers/object_completion_provider.rb +38 -0
- data/lib/platformos_check/language_server/completion_providers/render_snippet_completion_provider.rb +50 -0
- data/lib/platformos_check/language_server/completion_providers/tag_completion_provider.rb +41 -0
- data/lib/platformos_check/language_server/configuration.rb +89 -0
- data/lib/platformos_check/language_server/constants.rb +29 -0
- data/lib/platformos_check/language_server/diagnostic.rb +129 -0
- data/lib/platformos_check/language_server/diagnostics_engine.rb +131 -0
- data/lib/platformos_check/language_server/diagnostics_manager.rb +184 -0
- data/lib/platformos_check/language_server/document_change_corrector.rb +271 -0
- data/lib/platformos_check/language_server/document_link_engine.rb +21 -0
- data/lib/platformos_check/language_server/document_link_provider.rb +71 -0
- data/lib/platformos_check/language_server/document_link_providers/asset_document_link_provider.rb +11 -0
- data/lib/platformos_check/language_server/document_link_providers/include_document_link_provider.rb +11 -0
- data/lib/platformos_check/language_server/document_link_providers/render_document_link_provider.rb +11 -0
- data/lib/platformos_check/language_server/document_link_providers/section_document_link_provider.rb +11 -0
- data/lib/platformos_check/language_server/execute_command_engine.rb +19 -0
- data/lib/platformos_check/language_server/execute_command_provider.rb +30 -0
- data/lib/platformos_check/language_server/execute_command_providers/correction_execute_command_provider.rb +48 -0
- data/lib/platformos_check/language_server/execute_command_providers/run_checks_execute_command_provider.rb +28 -0
- data/lib/platformos_check/language_server/handler.rb +310 -0
- data/lib/platformos_check/language_server/hover_engine.rb +32 -0
- data/lib/platformos_check/language_server/hover_provider.rb +53 -0
- data/lib/platformos_check/language_server/hover_providers/filter_hover_provider.rb +113 -0
- data/lib/platformos_check/language_server/io_messenger.rb +109 -0
- data/lib/platformos_check/language_server/messenger.rb +27 -0
- data/lib/platformos_check/language_server/protocol.rb +55 -0
- data/lib/platformos_check/language_server/server.rb +188 -0
- data/lib/platformos_check/language_server/tokens.rb +55 -0
- data/lib/platformos_check/language_server/type_helper.rb +22 -0
- data/lib/platformos_check/language_server/uri_helper.rb +39 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/node_handler.rb +87 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope.rb +60 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope_visitor.rb +44 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder.rb +76 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/constants.rb +44 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/liquid_fixer.rb +103 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/potential_lookup.rb +10 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/tolerant_parser.rb +94 -0
- data/lib/platformos_check/language_server/variable_lookup_finder.rb +262 -0
- data/lib/platformos_check/language_server/variable_lookup_traverser.rb +70 -0
- data/lib/platformos_check/language_server/versioned_in_memory_storage.rb +84 -0
- data/lib/platformos_check/language_server.rb +71 -0
- data/lib/platformos_check/layout_file.rb +15 -0
- data/lib/platformos_check/liquid_check.rb +10 -0
- data/lib/platformos_check/liquid_file.rb +102 -0
- data/lib/platformos_check/liquid_node.rb +570 -0
- data/lib/platformos_check/liquid_visitor.rb +39 -0
- data/lib/platformos_check/migration_file.rb +9 -0
- data/lib/platformos_check/node.rb +53 -0
- data/lib/platformos_check/offense.rb +228 -0
- data/lib/platformos_check/page_file.rb +9 -0
- data/lib/platformos_check/parsing_helpers.rb +21 -0
- data/lib/platformos_check/partial_file.rb +15 -0
- data/lib/platformos_check/platformos_liquid/deprecated_filter.rb +31 -0
- data/lib/platformos_check/platformos_liquid/documentation/markdown_template.rb +51 -0
- data/lib/platformos_check/platformos_liquid/documentation.rb +45 -0
- data/lib/platformos_check/platformos_liquid/filter.rb +19 -0
- data/lib/platformos_check/platformos_liquid/object.rb +15 -0
- data/lib/platformos_check/platformos_liquid/source_index/base_entry.rb +66 -0
- data/lib/platformos_check/platformos_liquid/source_index/base_state.rb +23 -0
- data/lib/platformos_check/platformos_liquid/source_index/filter_entry.rb +26 -0
- data/lib/platformos_check/platformos_liquid/source_index/filter_state.rb +11 -0
- data/lib/platformos_check/platformos_liquid/source_index/object_entry.rb +20 -0
- data/lib/platformos_check/platformos_liquid/source_index/object_state.rb +11 -0
- data/lib/platformos_check/platformos_liquid/source_index/parameter_entry.rb +25 -0
- data/lib/platformos_check/platformos_liquid/source_index/property_entry.rb +21 -0
- data/lib/platformos_check/platformos_liquid/source_index/return_type_entry.rb +41 -0
- data/lib/platformos_check/platformos_liquid/source_index/tag_entry.rb +24 -0
- data/lib/platformos_check/platformos_liquid/source_index/tag_state.rb +11 -0
- data/lib/platformos_check/platformos_liquid/source_index.rb +79 -0
- data/lib/platformos_check/platformos_liquid/source_manager.rb +116 -0
- data/lib/platformos_check/platformos_liquid/tag.rb +59 -0
- data/lib/platformos_check/platformos_liquid.rb +21 -0
- data/lib/platformos_check/position.rb +180 -0
- data/lib/platformos_check/position_helper.rb +57 -0
- data/lib/platformos_check/printer.rb +87 -0
- data/lib/platformos_check/regex_helpers.rb +21 -0
- data/lib/platformos_check/releaser.rb +43 -0
- data/lib/platformos_check/schema_file.rb +6 -0
- data/lib/platformos_check/sms_file.rb +9 -0
- data/lib/platformos_check/storage.rb +29 -0
- data/lib/platformos_check/string_helpers.rb +48 -0
- data/lib/platformos_check/tags/background.rb +67 -0
- data/lib/platformos_check/tags/base.rb +14 -0
- data/lib/platformos_check/tags/base_block.rb +14 -0
- data/lib/platformos_check/tags/base_tag_methods.rb +59 -0
- data/lib/platformos_check/tags/cache.rb +13 -0
- data/lib/platformos_check/tags/export.rb +30 -0
- data/lib/platformos_check/tags/form.rb +19 -0
- data/lib/platformos_check/tags/function.rb +58 -0
- data/lib/platformos_check/tags/graphql.rb +70 -0
- data/lib/platformos_check/tags/hash_assign.rb +75 -0
- data/lib/platformos_check/tags/log.rb +15 -0
- data/lib/platformos_check/tags/parse_json.rb +24 -0
- data/lib/platformos_check/tags/print.rb +20 -0
- data/lib/platformos_check/tags/redirect_to.rb +15 -0
- data/lib/platformos_check/tags/render.rb +60 -0
- data/lib/platformos_check/tags/response_headers.rb +20 -0
- data/lib/platformos_check/tags/response_status.rb +20 -0
- data/lib/platformos_check/tags/return.rb +20 -0
- data/lib/platformos_check/tags/session.rb +27 -0
- data/lib/platformos_check/tags/sign_in.rb +27 -0
- data/lib/platformos_check/tags/spam_protection.rb +15 -0
- data/lib/platformos_check/tags/theme_render.rb +58 -0
- data/lib/platformos_check/tags/try.rb +59 -0
- data/lib/platformos_check/tags.rb +65 -0
- data/lib/platformos_check/translation_file.rb +6 -0
- data/lib/platformos_check/user_schema_file.rb +6 -0
- data/lib/platformos_check/version.rb +5 -0
- data/lib/platformos_check/yaml_check.rb +11 -0
- data/lib/platformos_check/yaml_file.rb +57 -0
- data/lib/platformos_check.rb +106 -0
- data/platformos-check.gemspec +34 -0
- metadata +329 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
# Reports missing include/render/section liquid file
|
|
5
|
+
class MissingTemplate < LiquidCheck
|
|
6
|
+
class MissingFileCorrection
|
|
7
|
+
def initialize(path:, directory:, extension:)
|
|
8
|
+
@path = path
|
|
9
|
+
@directory = directory
|
|
10
|
+
@extension = extension
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def full_relative_path
|
|
14
|
+
@full_relative_path ||= module? ? module_path : app_path
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
attr_reader :path, :directory, :extension
|
|
20
|
+
|
|
21
|
+
def app_path
|
|
22
|
+
['app', directory, "#{path}#{extension}"].join(File::SEPARATOR)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def module_path
|
|
26
|
+
path.split(File::SEPARATOR).insert(2, "public#{File::SEPARATOR}#{directory}").join(File::SEPARATOR) + extension
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def module?
|
|
30
|
+
path.start_with?('modules/')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
severity :suggestion
|
|
35
|
+
category :liquid
|
|
36
|
+
doc docs_url(__FILE__)
|
|
37
|
+
single_file false
|
|
38
|
+
|
|
39
|
+
def initialize(ignore_missing: [])
|
|
40
|
+
@ignore_missing = ignore_missing
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def on_include(node)
|
|
44
|
+
partial = node.value.template_name_expr
|
|
45
|
+
return unless partial.is_a?(String)
|
|
46
|
+
|
|
47
|
+
add_missing_partial_offense(partial, node:)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def on_render(node)
|
|
51
|
+
partial = node.value.template_name_expr
|
|
52
|
+
return unless partial.is_a?(String)
|
|
53
|
+
|
|
54
|
+
add_missing_partial_offense(partial, node:)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def on_function(node)
|
|
58
|
+
partial = node.value.from
|
|
59
|
+
return unless partial.is_a?(String)
|
|
60
|
+
|
|
61
|
+
add_missing_function_offense(partial, node:)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def on_graphql(node)
|
|
65
|
+
return if node.value.inline_query
|
|
66
|
+
|
|
67
|
+
graphql_partial = node.value.partial_name
|
|
68
|
+
return unless graphql_partial.is_a?(String)
|
|
69
|
+
|
|
70
|
+
add_missing_graphql_offense(graphql_partial, node:)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def ignore?(path)
|
|
76
|
+
all_ignored_patterns.any? { |pattern| File.fnmatch?(pattern, path) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def all_ignored_patterns
|
|
80
|
+
@all_ignored_patterns ||= @ignore_missing + ignored_patterns
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def add_missing_partial_offense(path, node:)
|
|
84
|
+
return if ignore?(path) || platformos_app.grouped_files[PartialFile][path]
|
|
85
|
+
|
|
86
|
+
add_offense("'#{path}' is not found", node:) # do |corrector|
|
|
87
|
+
# corrector.create_file(@platformos_app.storage, MissingFileCorrection.new(path:, directory: 'views/partials', extension: '.liquid').full_relative_path, "")
|
|
88
|
+
# end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def add_missing_function_offense(path, node:)
|
|
92
|
+
return if ignore?(path) || platformos_app.grouped_files[PartialFile][path]
|
|
93
|
+
|
|
94
|
+
add_offense("'#{path}' is not found", node:) # do |corrector|
|
|
95
|
+
# corrector.create_file(@platformos_app.storage, MissingFileCorrection.new(path:, directory: 'lib', extension: '.liquid').full_relative_path, "")
|
|
96
|
+
# end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def add_missing_graphql_offense(path, node:)
|
|
100
|
+
return if ignore?(path) || platformos_app.grouped_files[GraphqlFile][path]
|
|
101
|
+
|
|
102
|
+
add_offense("'#{path}' is not found", node:) # do |corrector|
|
|
103
|
+
# corrector.create_file(@platformos_app.storage, MissingFileCorrection.new(path:, directory: 'graphql', extension: '.graphql').full_relative_path, "")
|
|
104
|
+
# end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
class ParseJsonFormat < LiquidCheck
|
|
5
|
+
severity :style
|
|
6
|
+
category :liquid
|
|
7
|
+
doc docs_url(__FILE__)
|
|
8
|
+
|
|
9
|
+
def initialize(start_level: 0, indent: ' ')
|
|
10
|
+
@pretty_json_opts = {
|
|
11
|
+
indent:,
|
|
12
|
+
start_level:
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def on_parse_json(node)
|
|
17
|
+
parse_json = node.inner_json
|
|
18
|
+
return if parse_json.nil?
|
|
19
|
+
|
|
20
|
+
pretty_parse_json = pretty_json(parse_json, **@pretty_json_opts)
|
|
21
|
+
return unless pretty_parse_json != node.inner_markup
|
|
22
|
+
|
|
23
|
+
add_offense(
|
|
24
|
+
"JSON formatting could be improved",
|
|
25
|
+
node:
|
|
26
|
+
) do |corrector|
|
|
27
|
+
corrector.replace_inner_json(node, parse_json, **@pretty_json_opts)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
# Reports errors when trying to use parser-blocking script tags
|
|
5
|
+
class ParserBlockingJavaScript < HtmlCheck
|
|
6
|
+
severity :error
|
|
7
|
+
categories :html, :performance
|
|
8
|
+
doc docs_url(__FILE__)
|
|
9
|
+
|
|
10
|
+
def on_script(node)
|
|
11
|
+
return unless node.attributes["src"]
|
|
12
|
+
return if node.attributes["defer"] || node.attributes["async"] || node.attributes["type"] == "module"
|
|
13
|
+
|
|
14
|
+
add_offense("Missing async or defer attribute on script tag", node:)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
# Reports missing content_for_layout in layouts
|
|
5
|
+
class RequiredLayoutObject < LiquidCheck
|
|
6
|
+
severity :error
|
|
7
|
+
category :liquid
|
|
8
|
+
doc docs_url(__FILE__)
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@content_for_layout_found = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def on_document(node)
|
|
15
|
+
@layout_platformos_app_node = node if node.app_file.layout?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def on_variable(node)
|
|
19
|
+
return unless node.value.name.is_a?(Liquid::VariableLookup)
|
|
20
|
+
|
|
21
|
+
@content_for_layout_found ||= node.value.name.name == "content_for_layout"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def after_document(node)
|
|
25
|
+
return unless node.app_file.layout?
|
|
26
|
+
|
|
27
|
+
add_missing_object_offense("content_for_layout", "</body>") unless @content_for_layout_found
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def add_missing_object_offense(name, tag)
|
|
33
|
+
add_offense("layout must include {{#{name}}}", node: @layout_platformos_app_node) do
|
|
34
|
+
if @layout_platformos_app_node.source.index(tag)
|
|
35
|
+
@layout_platformos_app_node.source.insert(@layout_platformos_app_node.source.index(tag), " {{ #{name} }}\n ")
|
|
36
|
+
@layout_platformos_app_node.markup = @layout_platformos_app_node.source
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
# Ensure {% ... %} & {{ ... }} have consistent spaces.
|
|
5
|
+
class SpaceInsideBraces < LiquidCheck
|
|
6
|
+
severity :style
|
|
7
|
+
category :liquid
|
|
8
|
+
doc docs_url(__FILE__)
|
|
9
|
+
|
|
10
|
+
def on_node(node)
|
|
11
|
+
return unless node.markup
|
|
12
|
+
return if node.literal?
|
|
13
|
+
return if node.assigned_or_echoed_variable?
|
|
14
|
+
|
|
15
|
+
outside_of_strings(node.markup) do |chunk, chunk_start|
|
|
16
|
+
chunk.scan(/(?<token>[,:|]|==|<>|<=|>=|<|>|!=)(?<offense> +)/) do |_match|
|
|
17
|
+
add_too_many_spaces_after_offense(Regexp.last_match, node, chunk_start)
|
|
18
|
+
end
|
|
19
|
+
chunk.scan(/(?<offense>(?<token>[,:|]|==|<>|<=|>=|<\b|>\b|!=)(\S|\z))/) do |_match|
|
|
20
|
+
add_space_missing_after_offense(Regexp.last_match, node, chunk_start)
|
|
21
|
+
end
|
|
22
|
+
chunk.scan(/(?<offense>\s{2,})(?<token>\||==|<>|<=|>=|<|>|!=)+/) do |_match|
|
|
23
|
+
add_too_many_spaces_before_offense(Regexp.last_match, node, chunk_start) unless Regexp.last_match(:offense).include?("\n")
|
|
24
|
+
end
|
|
25
|
+
chunk.scan(/(\A|\S)(?<offense>(?<token>\||==|<>|<=|>=|<|\b>|!=))/) do |_match|
|
|
26
|
+
add_space_missing_before_offense(Regexp.last_match, node, chunk_start)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
BlockMarkup = Struct.new(:markup, :node_markup_offset)
|
|
32
|
+
|
|
33
|
+
def on_tag(node)
|
|
34
|
+
return if node.inside_liquid_tag?
|
|
35
|
+
|
|
36
|
+
# Both the start and end tags
|
|
37
|
+
blocks = [
|
|
38
|
+
BlockMarkup.new(node.block_start_markup, node.block_start_start_index - node.start_index),
|
|
39
|
+
BlockMarkup.new(node.block_end_markup, node.block_end_start_index - node.start_index)
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
blocks.each do |block|
|
|
43
|
+
# Looking at spaces after the start token
|
|
44
|
+
add_space_missing_after_offense(Regexp.last_match, node, block.node_markup_offset) if block.markup =~ /^(?<token>{%-?)(?<offense>[^ \n\t-])/
|
|
45
|
+
|
|
46
|
+
add_too_many_spaces_after_offense(Regexp.last_match, node, block.node_markup_offset) if block.markup =~ /^(?<token>{%-?)(?<offense> {2,})\S/
|
|
47
|
+
|
|
48
|
+
# Looking at spaces before the end token
|
|
49
|
+
add_space_missing_before_offense(Regexp.last_match, node, block.node_markup_offset) if block.markup =~ /(?<offense>[^ \n\t-])(?<token>-?%})$/
|
|
50
|
+
|
|
51
|
+
add_too_many_spaces_before_offense(Regexp.last_match, node, block.node_markup_offset) if block.markup =~ /\S(?<offense> {2,})(?<token>-?%})$/
|
|
52
|
+
|
|
53
|
+
next
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def on_variable(node)
|
|
58
|
+
return if node.markup.empty?
|
|
59
|
+
return if node.assigned_or_echoed_variable?
|
|
60
|
+
|
|
61
|
+
block_start_offset = node.block_start_start_index - node.start_index
|
|
62
|
+
|
|
63
|
+
# Looking at spaces after the start token
|
|
64
|
+
add_space_missing_after_offense(Regexp.last_match, node, block_start_offset) if node.block_start_markup =~ /^(?<token>{{-?)(?<offense>[^ \n\t-])/
|
|
65
|
+
|
|
66
|
+
add_too_many_spaces_after_offense(Regexp.last_match, node, block_start_offset) if node.block_start_markup =~ /^(?<token>{{-?)(?<offense> {2,})\S/
|
|
67
|
+
|
|
68
|
+
# Looking at spaces before the end token
|
|
69
|
+
add_space_missing_before_offense(Regexp.last_match, node, block_start_offset) if node.block_start_markup =~ /(?<offense>[^ \n\t-])(?<token>-?}})$/
|
|
70
|
+
|
|
71
|
+
return unless node.block_start_markup =~ /\S(?<offense> {2,})(?<token>-?}})$/
|
|
72
|
+
|
|
73
|
+
add_too_many_spaces_before_offense(Regexp.last_match, node, block_start_offset)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def add_space_missing_after_offense(match, node, source_offset)
|
|
77
|
+
add_offense_for_match(
|
|
78
|
+
"Space missing after '#{match[:token]}'",
|
|
79
|
+
match,
|
|
80
|
+
node,
|
|
81
|
+
source_offset
|
|
82
|
+
) do |corrector|
|
|
83
|
+
corrector.insert_after(
|
|
84
|
+
node,
|
|
85
|
+
' ',
|
|
86
|
+
(node.start_index + source_offset + match.begin(:token))...
|
|
87
|
+
(node.start_index + source_offset + match.end(:token))
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def add_too_many_spaces_after_offense(match, node, source_offset)
|
|
93
|
+
add_offense_for_match(
|
|
94
|
+
"Too many spaces after '#{match[:token]}'",
|
|
95
|
+
match,
|
|
96
|
+
node,
|
|
97
|
+
source_offset
|
|
98
|
+
) do |corrector|
|
|
99
|
+
corrector.replace(
|
|
100
|
+
node,
|
|
101
|
+
' ',
|
|
102
|
+
(node.start_index + source_offset + match.begin(:offense))...
|
|
103
|
+
(node.start_index + source_offset + match.end(:offense))
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def add_space_missing_before_offense(match, node, source_offset)
|
|
109
|
+
add_offense_for_match(
|
|
110
|
+
"Space missing before '#{match[:token]}'",
|
|
111
|
+
match,
|
|
112
|
+
node,
|
|
113
|
+
source_offset
|
|
114
|
+
) do |corrector|
|
|
115
|
+
corrector.insert_before(
|
|
116
|
+
node,
|
|
117
|
+
' ',
|
|
118
|
+
(node.start_index + source_offset + match.begin(:token))...
|
|
119
|
+
(node.start_index + source_offset + match.end(:token))
|
|
120
|
+
)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def add_too_many_spaces_before_offense(match, node, source_offset)
|
|
125
|
+
add_offense_for_match(
|
|
126
|
+
"Too many spaces before '#{match[:token]}'",
|
|
127
|
+
match,
|
|
128
|
+
node,
|
|
129
|
+
source_offset
|
|
130
|
+
) do |corrector|
|
|
131
|
+
corrector.replace(
|
|
132
|
+
node,
|
|
133
|
+
' ',
|
|
134
|
+
(node.start_index + source_offset + match.begin(:offense))...
|
|
135
|
+
(node.start_index + source_offset + match.end(:offense))
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def add_offense_for_match(message, match, node, source_offset, &)
|
|
141
|
+
add_offense(
|
|
142
|
+
message,
|
|
143
|
+
node:,
|
|
144
|
+
markup: match[:offense],
|
|
145
|
+
node_markup_offset: source_offset + match.begin(:offense),
|
|
146
|
+
&
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
# Report Liquid syntax errors
|
|
5
|
+
class SyntaxError < LiquidCheck
|
|
6
|
+
severity :error
|
|
7
|
+
category :liquid
|
|
8
|
+
doc docs_url(__FILE__)
|
|
9
|
+
|
|
10
|
+
def on_document(node)
|
|
11
|
+
node.app_file.warnings.each do |warning|
|
|
12
|
+
add_exception_as_offense(warning, app_file: node.app_file)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def on_error(exception)
|
|
17
|
+
add_exception_as_offense(exception, app_file: platformos_app[exception.template_name])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def add_exception_as_offense(exception, app_file:)
|
|
23
|
+
add_offense(
|
|
24
|
+
exception.to_s(false).sub(/ in ".*"$/, ''),
|
|
25
|
+
line_number: exception.line_number,
|
|
26
|
+
markup: exception.markup_context&.sub(/^in "(.*)"$/, '\1'),
|
|
27
|
+
app_file:
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
class TemplateLength < LiquidCheck
|
|
5
|
+
severity :suggestion
|
|
6
|
+
category :liquid
|
|
7
|
+
doc docs_url(__FILE__)
|
|
8
|
+
|
|
9
|
+
def initialize(max_length: 600)
|
|
10
|
+
@max_length = max_length
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def after_document(node)
|
|
14
|
+
lines = node.app_file.source.count("\n")
|
|
15
|
+
return unless lines > @max_length
|
|
16
|
+
|
|
17
|
+
add_offense("Template has too many lines [#{lines}/#{@max_length}]", app_file: node.app_file)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
class UndefinedObject < LiquidCheck
|
|
5
|
+
category :liquid
|
|
6
|
+
doc docs_url(__FILE__)
|
|
7
|
+
severity :error
|
|
8
|
+
|
|
9
|
+
class TemplateInfo
|
|
10
|
+
def initialize(app_file: nil)
|
|
11
|
+
@all_variable_lookups = {}
|
|
12
|
+
@all_assigns = {}
|
|
13
|
+
@all_captures = {}
|
|
14
|
+
@all_forloops = {}
|
|
15
|
+
@all_renders = {}
|
|
16
|
+
@app_file = app_file
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :all_assigns, :all_captures, :all_forloops, :app_file
|
|
20
|
+
|
|
21
|
+
def add_render(name:, node:)
|
|
22
|
+
@all_renders[name] = node
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_variable_lookup(name:, node:)
|
|
26
|
+
parent = node
|
|
27
|
+
line_number = nil
|
|
28
|
+
loop do
|
|
29
|
+
line_number = parent.line_number
|
|
30
|
+
parent = parent.parent
|
|
31
|
+
break unless line_number.nil? && parent
|
|
32
|
+
end
|
|
33
|
+
key = [name, line_number]
|
|
34
|
+
@all_variable_lookups[key] = node
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def all_variables
|
|
38
|
+
all_assigns.keys + all_captures.keys + all_forloops.keys
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def each_partial
|
|
42
|
+
@all_renders.each do |(name, info)|
|
|
43
|
+
yield [name, info]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def each_variable_lookup(unique_keys = false)
|
|
48
|
+
seen = Set.new
|
|
49
|
+
@all_variable_lookups.each do |(key, info)|
|
|
50
|
+
name, _line_number = key
|
|
51
|
+
|
|
52
|
+
next if unique_keys && seen.include?(name)
|
|
53
|
+
|
|
54
|
+
seen << name
|
|
55
|
+
|
|
56
|
+
yield [key, info]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def initialize(config_type: :default, exclude_partials: true)
|
|
62
|
+
@config_type = config_type
|
|
63
|
+
@exclude_partials = exclude_partials
|
|
64
|
+
@files = {}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def on_document(node)
|
|
68
|
+
return if ignore?(node)
|
|
69
|
+
|
|
70
|
+
@files[node.app_file.name] = TemplateInfo.new(app_file: node.app_file)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def on_assign(node)
|
|
74
|
+
return if ignore?(node)
|
|
75
|
+
|
|
76
|
+
@files[node.app_file.name].all_assigns[node.value.to] = node
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def on_capture(node)
|
|
80
|
+
return if ignore?(node)
|
|
81
|
+
|
|
82
|
+
@files[node.app_file.name].all_captures[node.value.instance_variable_get(:@to)] = node
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def on_parse_json(node)
|
|
86
|
+
return if ignore?(node)
|
|
87
|
+
|
|
88
|
+
@files[node.app_file.name].all_captures[node.value.to] = node
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def on_for(node)
|
|
92
|
+
return if ignore?(node)
|
|
93
|
+
|
|
94
|
+
@files[node.app_file.name].all_forloops[node.value.variable_name] = node
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def on_include(_node)
|
|
98
|
+
# NOOP: we purposely do nothing on `include` since it is deprecated
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def on_render(node)
|
|
102
|
+
return if ignore?(node)
|
|
103
|
+
return unless node.value.template_name_expr.is_a?(String)
|
|
104
|
+
|
|
105
|
+
partial_name = node.value.template_name_expr
|
|
106
|
+
@files[node.app_file.name].add_render(
|
|
107
|
+
name: partial_name,
|
|
108
|
+
node:
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def on_function(node)
|
|
113
|
+
return if ignore?(node)
|
|
114
|
+
|
|
115
|
+
name = node.value.from.is_a?(String) ? node.value.from : node.value.from.name
|
|
116
|
+
@files[node.app_file.name].add_render(
|
|
117
|
+
name:,
|
|
118
|
+
node:
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
@files[node.app_file.name].all_assigns[node.value.to] = node
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def on_graphql(node)
|
|
125
|
+
return if ignore?(node)
|
|
126
|
+
|
|
127
|
+
@files[node.app_file.name].all_assigns[node.value.to] = node
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def on_variable_lookup(node)
|
|
131
|
+
return if ignore?(node)
|
|
132
|
+
|
|
133
|
+
@files[node.app_file.name].add_variable_lookup(
|
|
134
|
+
name: node.value.name,
|
|
135
|
+
node:
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def on_end
|
|
140
|
+
all_global_objects = PlatformosCheck::PlatformosLiquid::Object.labels
|
|
141
|
+
all_global_objects.freeze
|
|
142
|
+
|
|
143
|
+
each_template do |(_name, info)|
|
|
144
|
+
if info.app_file.notification?
|
|
145
|
+
# NOTE: `data` comes from graphql for notifications
|
|
146
|
+
check_object(info, all_global_objects + %w[data response form])
|
|
147
|
+
else
|
|
148
|
+
check_object(info, all_global_objects)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
private
|
|
154
|
+
|
|
155
|
+
attr_reader :config_type
|
|
156
|
+
|
|
157
|
+
def ignore?(node)
|
|
158
|
+
@exclude_partials && node.app_file.partial?
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def each_template
|
|
162
|
+
@files.each do |(name, info)|
|
|
163
|
+
next if info.app_file.partial?
|
|
164
|
+
|
|
165
|
+
yield [name, info]
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def check_object(info, all_global_objects, render_node = nil, visited_partials = Set.new)
|
|
170
|
+
check_undefined(info, all_global_objects, render_node)
|
|
171
|
+
|
|
172
|
+
info.each_partial do |(partial_name, node)|
|
|
173
|
+
partial_info = @files[partial_name]
|
|
174
|
+
next unless partial_info # NOTE: undefined partial
|
|
175
|
+
|
|
176
|
+
partial_variables = node.value.attributes.keys +
|
|
177
|
+
[node.value.instance_variable_get(:@alias_name)]
|
|
178
|
+
unless visited_partials.include?(partial_name)
|
|
179
|
+
visited_partials << partial_name
|
|
180
|
+
check_object(partial_info, all_global_objects + partial_variables, node, visited_partials)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def check_undefined(info, all_global_objects, render_node)
|
|
186
|
+
all_variables = info.all_variables
|
|
187
|
+
|
|
188
|
+
info.each_variable_lookup(!!render_node) do |(key, node)|
|
|
189
|
+
name, line_number = key
|
|
190
|
+
next if all_variables.include?(name)
|
|
191
|
+
next if all_global_objects.include?(name)
|
|
192
|
+
|
|
193
|
+
node = node.parent
|
|
194
|
+
node = node.parent if %i[condition variable_lookup].include?(node.type_name)
|
|
195
|
+
|
|
196
|
+
next if node.variable? && node.filters.any? { |(filter_name)| filter_name == "default" }
|
|
197
|
+
|
|
198
|
+
if render_node
|
|
199
|
+
add_offense("Missing argument `#{name}`", node: render_node)
|
|
200
|
+
else
|
|
201
|
+
add_offense("Undefined object `#{name}`", node:, line_number:)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
#
|
|
5
|
+
# Unwanted:
|
|
6
|
+
#
|
|
7
|
+
# {{ x | some_unknown_filter }}
|
|
8
|
+
#
|
|
9
|
+
# Wanted:
|
|
10
|
+
#
|
|
11
|
+
# {{ x | upcase }}
|
|
12
|
+
#
|
|
13
|
+
class UnknownFilter < LiquidCheck
|
|
14
|
+
severity :error
|
|
15
|
+
category :liquid
|
|
16
|
+
doc docs_url(__FILE__)
|
|
17
|
+
|
|
18
|
+
def on_variable(node)
|
|
19
|
+
used_filters = node.filters.map { |name, *_rest| name }
|
|
20
|
+
undefined_filters = used_filters - PlatformosLiquid::Filter.labels - PlatformosLiquid::Filter.aliases
|
|
21
|
+
|
|
22
|
+
undefined_filters.each do |undefined_filter|
|
|
23
|
+
add_offense("Undefined filter `#{undefined_filter}`", node:)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|