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