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,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
|