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,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
class LayoutFile < LiquidFile
|
|
5
|
+
DIR_PREFIX = %r{\A/?((marketplace_builder|app)/(views/layouts)/|modules/((\w|-)*)/(private|public)/(views/layouts)/)}
|
|
6
|
+
|
|
7
|
+
def layout?
|
|
8
|
+
true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def dir_prefix
|
|
12
|
+
DIR_PREFIX
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
class LiquidFile < AppFile
|
|
5
|
+
def write
|
|
6
|
+
content = rewriter.to_s
|
|
7
|
+
return unless source != content
|
|
8
|
+
|
|
9
|
+
@storage.write(@relative_path, content.gsub("\n", @eol))
|
|
10
|
+
@source = content
|
|
11
|
+
@rewriter = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def liquid?
|
|
15
|
+
true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def template?
|
|
19
|
+
name.start_with?('template')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def notification?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def migration?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def page?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def partial?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def layout?
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def section?
|
|
43
|
+
name.start_with?('sections')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def snippet?
|
|
47
|
+
name.start_with?('snippet')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def rewriter
|
|
51
|
+
@rewriter ||= AppFileRewriter.new(@relative_path, source)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def source_excerpt(line)
|
|
55
|
+
original_lines = source.split("\n")
|
|
56
|
+
original_lines[bounded(0, line - 1, original_lines.size - 1)].strip
|
|
57
|
+
rescue StandardError => e
|
|
58
|
+
PlatformosCheck.bug(<<~EOS)
|
|
59
|
+
Exception while running `source_excerpt(#{line})`:
|
|
60
|
+
```
|
|
61
|
+
#{e.class}: #{e.message}
|
|
62
|
+
#{e.backtrace.join("\n ")}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
path: #{path}
|
|
66
|
+
|
|
67
|
+
source:
|
|
68
|
+
```
|
|
69
|
+
#{source}
|
|
70
|
+
```
|
|
71
|
+
EOS
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def parse
|
|
75
|
+
@ast ||= self.class.parse(source)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def warnings
|
|
79
|
+
@ast.warnings
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def root
|
|
83
|
+
parse.root
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.parse(source)
|
|
87
|
+
Tags.register_tags!
|
|
88
|
+
Liquid::Template.parse(
|
|
89
|
+
source,
|
|
90
|
+
line_numbers: true,
|
|
91
|
+
error_mode: :warn,
|
|
92
|
+
disable_liquid_c_nodes: true
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def bounded(lower, x, upper)
|
|
99
|
+
[lower, [x, upper].min].max
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
# A node from the Liquid AST, the result of parsing a liquid file.
|
|
5
|
+
class LiquidNode < Node
|
|
6
|
+
attr_reader :value, :parent, :app_file
|
|
7
|
+
|
|
8
|
+
def initialize(value, parent, app_file)
|
|
9
|
+
raise ArgumentError, "Expected a Liquid AST Node" if value.is_a?(LiquidNode)
|
|
10
|
+
|
|
11
|
+
@value = value
|
|
12
|
+
@parent = parent
|
|
13
|
+
@app_file = app_file
|
|
14
|
+
@tag_markup = nil
|
|
15
|
+
@line_number_offset = 0
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Array of children nodes.
|
|
19
|
+
def children
|
|
20
|
+
@children ||= begin
|
|
21
|
+
nodes =
|
|
22
|
+
if comment?
|
|
23
|
+
[]
|
|
24
|
+
elsif defined?(@value.class::ParseTreeVisitor)
|
|
25
|
+
@value.class::ParseTreeVisitor.new(@value, {}).children
|
|
26
|
+
elsif @value.respond_to?(:nodelist)
|
|
27
|
+
Array(@value.nodelist)
|
|
28
|
+
else
|
|
29
|
+
[]
|
|
30
|
+
end
|
|
31
|
+
# Work around a bug in Liquid::Variable::ParseTreeVisitor that doesn't return
|
|
32
|
+
# the args in a hash as children nodes.
|
|
33
|
+
nodes = nodes.flat_map do |node|
|
|
34
|
+
case node
|
|
35
|
+
when Hash
|
|
36
|
+
node.values
|
|
37
|
+
else
|
|
38
|
+
node
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
nodes
|
|
42
|
+
.reject(&:nil?) # We don't want nil nodes, and they can happen
|
|
43
|
+
.map { |node| LiquidNode.new(node, self, @app_file) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The original source code of the node. Doesn't contain wrapping braces.
|
|
48
|
+
def markup
|
|
49
|
+
if tag?
|
|
50
|
+
tag_markup
|
|
51
|
+
elsif literal?
|
|
52
|
+
value.to_s
|
|
53
|
+
elsif @value.instance_variable_defined?(:@markup)
|
|
54
|
+
@value.instance_variable_get(:@markup)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# The original source code of the node. Does contain wrapping braces.
|
|
59
|
+
def outer_markup
|
|
60
|
+
if literal?
|
|
61
|
+
markup
|
|
62
|
+
elsif variable_lookup?
|
|
63
|
+
''
|
|
64
|
+
elsif variable?
|
|
65
|
+
start_token + markup + end_token
|
|
66
|
+
elsif tag? && block?
|
|
67
|
+
start_index = block_start_start_index
|
|
68
|
+
end_index = block_start_end_index
|
|
69
|
+
end_index += inner_markup.size
|
|
70
|
+
end_index = find_block_delimiter(end_index)&.end(0)
|
|
71
|
+
source[start_index...end_index]
|
|
72
|
+
elsif tag?
|
|
73
|
+
source[block_start_start_index...block_start_end_index]
|
|
74
|
+
else
|
|
75
|
+
inner_markup
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def inner_markup
|
|
80
|
+
return '' unless block?
|
|
81
|
+
|
|
82
|
+
@inner_markup ||= source[block_start_end_index...block_end_start_index]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def inner_json
|
|
86
|
+
return nil unless parse_json?
|
|
87
|
+
|
|
88
|
+
@inner_json ||= JSON.parse(inner_markup)
|
|
89
|
+
rescue JSON::ParserError
|
|
90
|
+
# Handled by ValidSchema
|
|
91
|
+
@inner_json = nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def markup=(markup)
|
|
95
|
+
return unless @value.instance_variable_defined?(:@markup)
|
|
96
|
+
|
|
97
|
+
@value.instance_variable_set(:@markup, markup)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Most nodes have a line number, but it's not guaranteed.
|
|
101
|
+
def line_number
|
|
102
|
+
if tag? && @value.respond_to?(:line_number)
|
|
103
|
+
markup # initialize the line_number_offset
|
|
104
|
+
@value.line_number - @line_number_offset
|
|
105
|
+
elsif @value.respond_to?(:line_number)
|
|
106
|
+
@value.line_number
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def start_index
|
|
111
|
+
position.start_index
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def start_row
|
|
115
|
+
position.start_row
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def start_column
|
|
119
|
+
position.start_column
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def end_index
|
|
123
|
+
position.end_index
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def end_row
|
|
127
|
+
position.end_row
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def end_column
|
|
131
|
+
position.end_column
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Literals are hard-coded values in the liquid file.
|
|
135
|
+
def literal?
|
|
136
|
+
@value.is_a?(String) || @value.is_a?(Integer)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# A {% tag %} node?
|
|
140
|
+
def tag?
|
|
141
|
+
@value.is_a?(Liquid::Tag)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def variable?
|
|
145
|
+
@value.is_a?(Liquid::Variable)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def assigned_or_echoed_variable?
|
|
149
|
+
variable? && start_token == ""
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def variable_lookup?
|
|
153
|
+
@value.is_a?(Liquid::VariableLookup)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# A {% comment %} block node?
|
|
157
|
+
def comment?
|
|
158
|
+
@value.is_a?(Liquid::Comment)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# {% # comment %}
|
|
162
|
+
def inline_comment?
|
|
163
|
+
@value.is_a?(Liquid::InlineComment)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Top level node of every liquid_file.
|
|
167
|
+
def document?
|
|
168
|
+
@value.is_a?(Liquid::Document)
|
|
169
|
+
end
|
|
170
|
+
alias root? document?
|
|
171
|
+
|
|
172
|
+
# A {% tag %}...{% endtag %} node?
|
|
173
|
+
def block_tag?
|
|
174
|
+
@value.is_a?(Liquid::Block)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# The body of blocks
|
|
178
|
+
def block_body?
|
|
179
|
+
@value.is_a?(Liquid::BlockBody)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# A block of type of node?
|
|
183
|
+
def block?
|
|
184
|
+
block_tag? || block_body? || document?
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def parse_json?
|
|
188
|
+
@value.is_a?(PlatformosCheck::Tags::ParseJson)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def function?
|
|
192
|
+
@value.is_a?(PlatformosCheck::Tags::Function)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# The `:under_score_name` of this type of node. Used to dispatch to the `on_<type_name>`
|
|
196
|
+
# and `after_<type_name>` check methods.
|
|
197
|
+
def type_name
|
|
198
|
+
@type_name ||= StringHelpers.underscore(StringHelpers.demodulize(@value.class.name)).to_sym
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def filters
|
|
202
|
+
raise TypeError, "Attempting to lookup filters of #{type_name}. Only variables have filters." unless variable?
|
|
203
|
+
|
|
204
|
+
@value.filters
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def source
|
|
208
|
+
app_file&.source
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# For debugging purposes, this might be easier for the eyes.
|
|
212
|
+
def to_h
|
|
213
|
+
if literal?
|
|
214
|
+
return @value
|
|
215
|
+
elsif variable_lookup?
|
|
216
|
+
return {
|
|
217
|
+
type_name:,
|
|
218
|
+
name: value.name.to_s,
|
|
219
|
+
lookups: children.map(&:to_h)
|
|
220
|
+
}
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
{
|
|
224
|
+
type_name:,
|
|
225
|
+
markup: outer_markup,
|
|
226
|
+
children: children.map(&:to_h)
|
|
227
|
+
}
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def block_start_markup
|
|
231
|
+
source[block_start_start_index...block_start_end_index]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def block_start_start_index
|
|
235
|
+
@block_start_start_index ||= if inside_liquid_tag?
|
|
236
|
+
backtrack_on_whitespace(source, start_index, /[ \t]/)
|
|
237
|
+
elsif tag?
|
|
238
|
+
backtrack_on_whitespace(source, start_index) - start_token.length
|
|
239
|
+
else
|
|
240
|
+
position.start_index - start_token.length
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def block_start_end_index
|
|
245
|
+
@block_start_end_index ||= position.end_index + end_token.size
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def block_end_markup
|
|
249
|
+
source[block_end_start_index...block_end_end_index]
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def block_end_start_index
|
|
253
|
+
return block_start_end_index unless tag? && block?
|
|
254
|
+
|
|
255
|
+
@block_end_start_index ||= block_end_match&.begin(0) || block_start_end_index
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def block_end_end_index
|
|
259
|
+
return block_end_start_index unless tag? && block?
|
|
260
|
+
|
|
261
|
+
@block_end_end_index ||= block_end_match&.end(0) || block_start_end_index
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def outer_markup_start_index
|
|
265
|
+
outer_markup_position.start_index
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def outer_markup_end_index
|
|
269
|
+
outer_markup_position.end_index
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def outer_markup_start_row
|
|
273
|
+
outer_markup_position.start_row
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def outer_markup_start_column
|
|
277
|
+
outer_markup_position.start_column
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def outer_markup_end_row
|
|
281
|
+
outer_markup_position.end_row
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def outer_markup_end_column
|
|
285
|
+
outer_markup_position.end_column
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def inner_markup_start_index
|
|
289
|
+
inner_markup_position.start_index
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def inner_markup_end_index
|
|
293
|
+
inner_markup_position.end_index
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def inner_markup_start_row
|
|
297
|
+
inner_markup_position.start_row
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def inner_markup_start_column
|
|
301
|
+
inner_markup_position.start_column
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def inner_markup_end_row
|
|
305
|
+
inner_markup_position.end_row
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def inner_markup_end_column
|
|
309
|
+
inner_markup_position.end_column
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
WHITESPACE = /\s/
|
|
313
|
+
|
|
314
|
+
# Is this node inside a `{% liquid ... %}` block?
|
|
315
|
+
def inside_liquid_tag?
|
|
316
|
+
# What we're doing here is starting at the start of the tag and
|
|
317
|
+
# backtrack on all the whitespace until we land on something. If
|
|
318
|
+
# that something is {% or %-, then we can safely assume that
|
|
319
|
+
# we're inside a full tag and not a liquid tag.
|
|
320
|
+
@inside_liquid_tag ||= if tag? && start_index && source
|
|
321
|
+
i = 1
|
|
322
|
+
i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
|
|
323
|
+
first_two_backtracked_characters = source[(start_index - i - 1)..(start_index - i)]
|
|
324
|
+
first_two_backtracked_characters != "{%" && first_two_backtracked_characters != "%-"
|
|
325
|
+
else
|
|
326
|
+
false
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Is this node inside a tag or variable that starts by removing whitespace. i.e. {%- or {{-
|
|
331
|
+
def whitespace_trimmed_start?
|
|
332
|
+
@whitespace_trimmed_start ||= if start_index && source && !inside_liquid_tag?
|
|
333
|
+
i = 1
|
|
334
|
+
i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
|
|
335
|
+
source[start_index - i] == "-"
|
|
336
|
+
else
|
|
337
|
+
false
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Is this node inside a tag or variable ends starts by removing whitespace. i.e. -%} or -}}
|
|
342
|
+
def whitespace_trimmed_end?
|
|
343
|
+
@whitespace_trimmed_end ||= if end_index && source && !inside_liquid_tag?
|
|
344
|
+
i = 0
|
|
345
|
+
i += 1 while source[end_index + i] =~ WHITESPACE && i < source.size
|
|
346
|
+
source[end_index + i] == "-"
|
|
347
|
+
else
|
|
348
|
+
false
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def start_token
|
|
353
|
+
if inside_liquid_tag?
|
|
354
|
+
""
|
|
355
|
+
elsif variable? && source[start_index - 3..start_index - 1] == "{{-"
|
|
356
|
+
"{{-"
|
|
357
|
+
elsif variable? && source[start_index - 2..start_index - 1] == "{{"
|
|
358
|
+
"{{"
|
|
359
|
+
elsif tag? && whitespace_trimmed_start?
|
|
360
|
+
"{%-"
|
|
361
|
+
elsif tag?
|
|
362
|
+
"{%"
|
|
363
|
+
else
|
|
364
|
+
""
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def end_token
|
|
369
|
+
if inside_liquid_tag? && source[end_index] == "\n"
|
|
370
|
+
"\n"
|
|
371
|
+
elsif inside_liquid_tag?
|
|
372
|
+
""
|
|
373
|
+
elsif variable? && source[end_index...end_index + 3] == "-}}"
|
|
374
|
+
"-}}"
|
|
375
|
+
elsif variable? && source[end_index...end_index + 2] == "}}"
|
|
376
|
+
"}}"
|
|
377
|
+
elsif tag? && whitespace_trimmed_end?
|
|
378
|
+
"-%}"
|
|
379
|
+
elsif tag?
|
|
380
|
+
"%}"
|
|
381
|
+
else # this could happen because we're in an assign statement (variable)
|
|
382
|
+
""
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
private
|
|
387
|
+
|
|
388
|
+
def position
|
|
389
|
+
@position ||= Position.new(
|
|
390
|
+
markup,
|
|
391
|
+
app_file&.source,
|
|
392
|
+
line_number_1_indexed: line_number
|
|
393
|
+
)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def outer_markup_position
|
|
397
|
+
@outer_markup_position ||= StrictPosition.new(
|
|
398
|
+
outer_markup,
|
|
399
|
+
source,
|
|
400
|
+
block_start_start_index
|
|
401
|
+
)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def inner_markup_position
|
|
405
|
+
@inner_markup_position ||= StrictPosition.new(
|
|
406
|
+
inner_markup,
|
|
407
|
+
source,
|
|
408
|
+
block_start_end_index
|
|
409
|
+
)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Here we're hacking around a glorious bug in Liquid that makes it so the
|
|
413
|
+
# line_number and markup of a tag is wrong if there's whitespace
|
|
414
|
+
# between the tag_name and the markup of the tag.
|
|
415
|
+
#
|
|
416
|
+
# {%
|
|
417
|
+
# render
|
|
418
|
+
# 'foo'
|
|
419
|
+
# %}
|
|
420
|
+
#
|
|
421
|
+
# Returns a raw value of "render 'foo'\n".
|
|
422
|
+
# The "\n " between render and 'foo' got replaced by a single space.
|
|
423
|
+
#
|
|
424
|
+
# And the line number is the one of 'foo'\n%}. Yay!
|
|
425
|
+
#
|
|
426
|
+
# This breaks any kind of position logic we have since that string
|
|
427
|
+
# does not exist in the app_file.
|
|
428
|
+
def tag_markup
|
|
429
|
+
return @tag_markup if @tag_markup
|
|
430
|
+
|
|
431
|
+
l = 1
|
|
432
|
+
scanner = StringScanner.new(source)
|
|
433
|
+
scanner.scan_until(/\n/) while l < @value.line_number && (l += 1)
|
|
434
|
+
start = scanner.charpos
|
|
435
|
+
|
|
436
|
+
tag_name = @value.tag_name
|
|
437
|
+
tag_markup = @value.instance_variable_get(:@markup)
|
|
438
|
+
|
|
439
|
+
# This is tricky, if the tag_markup is empty, then the tag could
|
|
440
|
+
# either start on a previous line, or the tag could start on the
|
|
441
|
+
# same line.
|
|
442
|
+
#
|
|
443
|
+
# Consider this:
|
|
444
|
+
# 1 {%
|
|
445
|
+
# 2 comment
|
|
446
|
+
# 3 %}{% endcomment %}{%comment%}
|
|
447
|
+
#
|
|
448
|
+
# Both comments would markup == "" AND line_number == 3
|
|
449
|
+
#
|
|
450
|
+
# There's no way to determine which one is the correct one, but
|
|
451
|
+
# we'll try our best to at least give you one.
|
|
452
|
+
#
|
|
453
|
+
# To screw with you even more, the name of the tag could be
|
|
454
|
+
# outside of a tag on the same line :) But I won't do anything
|
|
455
|
+
# about that (yet?).
|
|
456
|
+
#
|
|
457
|
+
# {% comment
|
|
458
|
+
# %}comment{% endcomment %}
|
|
459
|
+
if tag_markup.empty?
|
|
460
|
+
eol = source.index("\n", start) || source.size
|
|
461
|
+
|
|
462
|
+
# OK here I'm trying one of two things. Either tag_start is on
|
|
463
|
+
# the same line OR tag_start is on a previous line. The line
|
|
464
|
+
# number would be at the end of the whitespace after tag_name.
|
|
465
|
+
unless (tag_start = source.index(tag_name, start)) && tag_start < eol
|
|
466
|
+
tag_start = start
|
|
467
|
+
tag_start -= 1 while source[tag_start - 1] =~ WHITESPACE
|
|
468
|
+
tag_start -= @value.tag_name.size
|
|
469
|
+
|
|
470
|
+
# keep track of the error in line_number
|
|
471
|
+
@line_number_offset = source[tag_start...start].count("\n")
|
|
472
|
+
end
|
|
473
|
+
tag_end = tag_start + tag_name.size
|
|
474
|
+
tag_end += 1 while source[tag_end] =~ WHITESPACE
|
|
475
|
+
|
|
476
|
+
# return the real raw content
|
|
477
|
+
@tag_markup = source[tag_start...tag_end]
|
|
478
|
+
return @tag_markup
|
|
479
|
+
|
|
480
|
+
# because line_numbers are not enough to accurately
|
|
481
|
+
# determine the position of the raw markup and because that
|
|
482
|
+
# markup could be present on the same line outside of a Tag. e.g.
|
|
483
|
+
#
|
|
484
|
+
# uhoh {% if uhoh %}
|
|
485
|
+
elsif (match = /#{tag_name} +#{Regexp.escape(tag_markup)}/.match(source, start))
|
|
486
|
+
return @tag_markup = match[0]
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# find the markup
|
|
490
|
+
markup_start = source.index(tag_markup, start)
|
|
491
|
+
markup_end = markup_start + tag_markup.size
|
|
492
|
+
|
|
493
|
+
# go back until you find the tag_name
|
|
494
|
+
tag_start = markup_start
|
|
495
|
+
tag_start -= 1 while source[tag_start - 1] =~ WHITESPACE
|
|
496
|
+
tag_start -= tag_name.size
|
|
497
|
+
|
|
498
|
+
# keep track of the error in line_number
|
|
499
|
+
@line_number_offset = source[tag_start...markup_start].count("\n")
|
|
500
|
+
|
|
501
|
+
# return the real raw content
|
|
502
|
+
@tag_markup = source[tag_start...markup_end]
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
# Returns the index of the leftmost consecutive whitespace
|
|
506
|
+
# starting from start going backwards.
|
|
507
|
+
#
|
|
508
|
+
# e.g. backtrack_on_whitespace("01 45", 4) would return 2.
|
|
509
|
+
# e.g. backtrack_on_whitespace("{% render %}", 5) would return 2.
|
|
510
|
+
def backtrack_on_whitespace(string, start, whitespace = WHITESPACE)
|
|
511
|
+
i = start
|
|
512
|
+
i -= 1 while string[i - 1] =~ whitespace && i > 0
|
|
513
|
+
i
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def find_block_delimiter(start_index)
|
|
517
|
+
return nil unless tag? && block?
|
|
518
|
+
|
|
519
|
+
tag_start, tag_end = if inside_liquid_tag?
|
|
520
|
+
[
|
|
521
|
+
/^\s*#{@value.tag_name}\s*/,
|
|
522
|
+
/^\s*end#{@value.tag_name}\s*/
|
|
523
|
+
]
|
|
524
|
+
else
|
|
525
|
+
[
|
|
526
|
+
/#{Liquid::TagStart}-?\s*#{@value.tag_name}/mi,
|
|
527
|
+
/#{Liquid::TagStart}-?\s*end#{@value.tag_name}\s*-?#{Liquid::TagEnd}/mi
|
|
528
|
+
]
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
# This little algorithm below find the _correct_ block delimiter
|
|
532
|
+
# (endif, endcase, endcomment) for the current tag. What do I
|
|
533
|
+
# mean by correct? It means the one you'd expect. Making sure
|
|
534
|
+
# that we don't do the naive regex find. Since you can have
|
|
535
|
+
# nested ifs, fors, etc.
|
|
536
|
+
#
|
|
537
|
+
# It works by having a stack, pushing onto the stack when we
|
|
538
|
+
# open a tag of our type_name. And popping when we find a
|
|
539
|
+
# closing tag of our type_name.
|
|
540
|
+
#
|
|
541
|
+
# When the stack is empty, we return the end tag match.
|
|
542
|
+
index = start_index
|
|
543
|
+
stack = []
|
|
544
|
+
stack.push("open")
|
|
545
|
+
loop do
|
|
546
|
+
tag_start_match = tag_start.match(source, index)
|
|
547
|
+
tag_end_match = tag_end.match(source, index)
|
|
548
|
+
|
|
549
|
+
return nil unless tag_end_match
|
|
550
|
+
|
|
551
|
+
# We have found a tag_start and it appeared _before_ the
|
|
552
|
+
# tag_end that we found, thus we push it onto the stack.
|
|
553
|
+
stack.push("open") if tag_start_match && tag_start_match.end(0) < tag_end_match.end(0)
|
|
554
|
+
|
|
555
|
+
# We have found a tag_end, therefore we pop
|
|
556
|
+
stack.pop
|
|
557
|
+
|
|
558
|
+
# Nothing left on the stack, we're done.
|
|
559
|
+
break tag_end_match if stack.empty?
|
|
560
|
+
|
|
561
|
+
# We keep looking from the end of the end tag we just found.
|
|
562
|
+
index = tag_end_match.end(0)
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
def block_end_match
|
|
567
|
+
@block_end_match ||= find_block_delimiter(block_start_end_index)
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
end
|