platformos-check 0.4.7 → 0.4.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -1
- data/README.md +13 -4
- data/config/default.yml +8 -1
- data/docs/checks/form_action.md +6 -0
- data/docs/checks/form_authenticity_token.md +22 -3
- data/docs/checks/graphql_in_for_loop.md +56 -0
- data/docs/checks/include_in_render.md +62 -0
- data/lib/platformos_check/analyzer.rb +6 -1
- data/lib/platformos_check/checks/convert_include_to_render.rb +41 -2
- data/lib/platformos_check/checks/form_action.rb +3 -1
- data/lib/platformos_check/checks/form_authenticity_token.rb +20 -0
- data/lib/platformos_check/checks/graphql_in_for_loop.rb +108 -0
- data/lib/platformos_check/checks/img_lazy_loading.rb +6 -2
- data/lib/platformos_check/checks/include_in_render.rb +45 -0
- data/lib/platformos_check/checks/invalid_args.rb +4 -1
- data/lib/platformos_check/checks/undefined_object.rb +55 -26
- data/lib/platformos_check/checks/unreachable_code.rb +9 -10
- data/lib/platformos_check/checks/unused_assign.rb +33 -24
- data/lib/platformos_check/cli.rb +1 -1
- data/lib/platformos_check/graphql_file.rb +4 -0
- data/lib/platformos_check/liquid_file.rb +1 -1
- data/lib/platformos_check/tags/context.rb +13 -0
- data/lib/platformos_check/tags/graphql.rb +3 -0
- data/lib/platformos_check/tags.rb +2 -0
- data/lib/platformos_check/version.rb +1 -1
- data/lib/platformos_check.rb +1 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e41eee4e2aa717be9663df4efbc1acf79bc8b571784808b80177f9298fdad9e9
|
4
|
+
data.tar.gz: 6bed8f9e1e3e22dbd02cee00a15d5e9ce1787f7826ee09e3e461fcf0161c681d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71a9feab2a3d5e31a2436652036bf1669c65424f24cfa550feedb49bd43e03264f2feb51b0367c84a79805c3cda0d24ed3ccccdd27d2b63a09dd15f3e26a94b4
|
7
|
+
data.tar.gz: 56961261716dae13fce53dadb2c6e03d8c3f268195748e81783cfe4dd7162c18ffb05063530975939ad2805f2a2e33d5d63aab1da51de65f3c98e17e1e2df2b4
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,30 @@
|
|
1
|
-
v0.4.
|
1
|
+
v0.4.9 / 2024-01-10
|
2
|
+
==================
|
3
|
+
|
4
|
+
* Skip FormAuthenticityToken check for GET forms
|
5
|
+
* Skip FormAuthenticityToken for action which is not relative path
|
6
|
+
* Fix FormAction to not report offenses on valid scenarios
|
7
|
+
* UnusedAssign will not automatically remove assign if it might change the business logic (which is a scenario when filters modifying objects are used)
|
8
|
+
* UnusedAssign will automatically rename result of background tag if variable not used
|
9
|
+
* Fix reporting UndefinedObject's missing argument offenses when the same partial is used multiple times (previously offenses where displayed only for the last render)
|
10
|
+
* Add autocorrector for UndefinedObject's missing argument error (explicitly provide null)
|
11
|
+
* Add autocorrector for ImgLazyLoading
|
12
|
+
* ConvertIncludeToRender will not report offense as autocorrect
|
13
|
+
* Improve inline GraphQL syntax check to raise error if result variable not provided
|
14
|
+
* Add autocorrector for UndefinedObject (Unused Argument offense) (FIXME: for N unused arguments in the same line it needs to be invoked N times)
|
15
|
+
* Add autocorrector for InvalidArgs - remove duplicates arguments
|
16
|
+
* Do not report ConvertIncludeToRender offenses for valid use cases (using `break` and using variable as a template name)
|
17
|
+
* Add IncludeInRender check
|
18
|
+
* Improve autocorrector for UndefinedObject's missing argument error - if variable is defined, it will be passed instead of hardcoding null
|
19
|
+
* Re-enable autocorrector for ConvertIncludeToRender
|
20
|
+
* Make UndefinedObject more clever - it will report undefined object if variable is used before declaration
|
21
|
+
|
22
|
+
v0.4.8 / 2023-12-20
|
23
|
+
==================
|
24
|
+
|
25
|
+
* Add GraphqlInForLoop check
|
26
|
+
|
27
|
+
v0.4.7 / 2023-12-27
|
2
28
|
==================
|
3
29
|
|
4
30
|
* Add UnreachableCode check
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ PlatformOS Check currently checks for the following:
|
|
11
11
|
✅ Liquid syntax errors
|
12
12
|
✅ JSON syntax errors
|
13
13
|
✅ Missing partials and graphqls
|
14
|
-
✅ Unused `{% assign ... %}
|
14
|
+
✅ Unused variables (via `{% assign var = ... %}`, {% function var = ... %} etc.)
|
15
15
|
✅ Unused partials
|
16
16
|
✅ Template length
|
17
17
|
✅ Deprecated tags
|
@@ -23,14 +23,14 @@ PlatformOS Check currently checks for the following:
|
|
23
23
|
✅ Deprecated filters
|
24
24
|
✅ Missing `platformos-check-enable` comment
|
25
25
|
✅ Invalid arguments provided to `{% graphql %}` tags
|
26
|
+
✅ Missing `authenticity_token` in `<form>`
|
27
|
+
✅ Unreachable code
|
26
28
|
|
27
29
|
As well as checks that prevent easy to spot performance problems:
|
28
30
|
|
31
|
+
✅ [GraphQL in for loop](/docs/checks/graphql_in_for_loop.md)
|
29
32
|
✅ Use of [parser-blocking](/docs/checks/parser_blocking_javascript.md) JavaScript
|
30
|
-
✅ [Use of non-platformOS domains for assets](/docs/checks/remote_asset.md)
|
31
33
|
✅ [Missing width and height attributes on `img` tags](/docs/checks/img_width_and_height.md)
|
32
|
-
✅ [Too much JavaScript](/docs/checks/asset_size_javascript.md)
|
33
|
-
✅ [Too much CSS](/docs/checks/asset_size_css.md)
|
34
34
|
|
35
35
|
For detailed descriptions and configuration options, [take a look at the complete list.](/docs/checks/)
|
36
36
|
|
@@ -52,12 +52,21 @@ With more to come! Suggestions welcome ([create an issue](https://github.com/Pla
|
|
52
52
|
### Install ruby and platform-check gem
|
53
53
|
|
54
54
|
1. Download the latest version of Ruby - https://www.ruby-lang.org/en/documentation/installation/
|
55
|
+
|
56
|
+
Verify that you've installed at least version 3.2:
|
57
|
+
|
58
|
+
`ruby -v`
|
59
|
+
|
60
|
+
⚠️ **Note:** You might need to restart the terminal after installing.
|
61
|
+
⚠️ **Note:*** Please make sure you install ruby for your user, not the root
|
62
|
+
|
55
63
|
2. Install platformos-check gem
|
56
64
|
|
57
65
|
`gem install platformos-check`
|
58
66
|
|
59
67
|
You can verify the installation was successful by invoking `platformos-check --version`. If you chose this method, use `platformos-check-language-server` as a path to your language server instead of `/Users/<username/platformos-check-language-server`
|
60
68
|
|
69
|
+
⚠️ **Note:*** Please make sure you install the gem for your user, not the root - i.e. without `sudo`
|
61
70
|
|
62
71
|
### Using Docker
|
63
72
|
|
data/config/default.yml
CHANGED
@@ -15,6 +15,10 @@ ConvertIncludeToRender:
|
|
15
15
|
enabled: true
|
16
16
|
ignore: []
|
17
17
|
|
18
|
+
IncludeInRender:
|
19
|
+
enabled: true
|
20
|
+
ignore: []
|
21
|
+
|
18
22
|
LiquidTag:
|
19
23
|
enabled: true
|
20
24
|
ignore: []
|
@@ -69,7 +73,6 @@ ValidYaml:
|
|
69
73
|
UndefinedObject:
|
70
74
|
enabled: true
|
71
75
|
ignore: []
|
72
|
-
config_type: :default
|
73
76
|
|
74
77
|
DeprecatedFilter:
|
75
78
|
enabled: true
|
@@ -99,6 +102,10 @@ FormAuthenticityToken:
|
|
99
102
|
enabled: true
|
100
103
|
ignore: []
|
101
104
|
|
105
|
+
GraphqlInForLoop:
|
106
|
+
enabled: true
|
107
|
+
ignore: []
|
108
|
+
|
102
109
|
HtmlParsingError:
|
103
110
|
enabled: true
|
104
111
|
ignore: []
|
data/docs/checks/form_action.md
CHANGED
@@ -30,6 +30,12 @@ This check is aimed at ensuring you have not forgotten to start the path with /.
|
|
30
30
|
</form>
|
31
31
|
```
|
32
32
|
|
33
|
+
```liquid
|
34
|
+
<form action="https://example.com/external">
|
35
|
+
...
|
36
|
+
</form>
|
37
|
+
```
|
38
|
+
|
33
39
|
## Check Options
|
34
40
|
|
35
41
|
The default configuration for this check is the following:
|
@@ -3,7 +3,7 @@
|
|
3
3
|
In platformOS all POST/PATCH/PUT/DELETE requests are protected from [CSRF Attacks][csrf-attack] through [authenticity_token][page-csrf]
|
4
4
|
Form action defines the endpoint to which browser will make a request after submitting it.
|
5
5
|
|
6
|
-
As a general rule you should include hidden input `<input type="hidden" name="authenticity_token" value="{{ context.authenticity_token }}">` in every form. Missing it will result in session invalidation and
|
6
|
+
As a general rule you should include hidden input `<input type="hidden" name="authenticity_token" value="{{ context.authenticity_token }}">` in every form. Missing it will result in session invalidation and the logged in user will be automatically logged out.
|
7
7
|
|
8
8
|
## Check Details
|
9
9
|
|
@@ -12,18 +12,37 @@ This check is aimed at ensuring you have not forgotten to include authenticity_t
|
|
12
12
|
:-1: Examples of **incorrect** code for this check:
|
13
13
|
|
14
14
|
```liquid
|
15
|
-
<form action="dummy/create">
|
15
|
+
<form action="/dummy/create" method="post">
|
16
16
|
</form>
|
17
17
|
```
|
18
18
|
|
19
19
|
:+1: Examples of **correct** code for this check:
|
20
20
|
|
21
|
+
With token:
|
21
22
|
```liquid
|
22
|
-
<form action="/dummy/create">
|
23
|
+
<form action="/dummy/create" method="post">
|
23
24
|
<input type="hidden" name="authenticity_token" value="{{ context.authenticity_token }}">
|
24
25
|
</form>
|
25
26
|
```
|
26
27
|
|
28
|
+
For GET request:
|
29
|
+
```liquid
|
30
|
+
<form action="/dummy/create">
|
31
|
+
</form>
|
32
|
+
```
|
33
|
+
|
34
|
+
For external request:
|
35
|
+
```liquid
|
36
|
+
<form action="https://example.com/dummy/create" method="post">
|
37
|
+
</form>
|
38
|
+
```
|
39
|
+
|
40
|
+
For parameterized request:
|
41
|
+
```liquid
|
42
|
+
<form action="{{ context.constants.MY_REQUEST_URL }}" method="post">
|
43
|
+
</form>
|
44
|
+
```
|
45
|
+
|
27
46
|
## Check Options
|
28
47
|
|
29
48
|
The default configuration for this check is the following:
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# GraphQL in for loop (`GraphqlInForLoop`)
|
2
|
+
|
3
|
+
This check is aimed towards identifying performance problems before they arise. Invoking GraphQL queries/mutations inside the `for loop` can lead to performance issues such as increased database load, higher latency, and inefficient use of network resources. It is particularly problematic when dealing with large datasets or when the number of entities grows.
|
4
|
+
|
5
|
+
Invoking a GraphQL query within a `for loop` might also be a sign of a so called N+1 problem. The N+1 pattern often arises when dealing with relationships between entities. In an attempt to retrieve related data, developers may inadvertently end up executing a large number of queries, resulting in a significant performance overhead.
|
6
|
+
|
7
|
+
To address this problem, developers can use techniques like eager loading, which involves fetching the related data in a single query instead of issuing separate queries for each entity. This helps reduce the number of database round-trips and improves overall system performance. In platformOS the most common technique to fix the N+1 issue is by [using related_records][relalted_records].
|
8
|
+
|
9
|
+
## Check Details
|
10
|
+
|
11
|
+
This check is aimed towards identifying GraphQL queries invoked within a for loop.
|
12
|
+
|
13
|
+
:-1: Examples of **incorrect** code for this check:
|
14
|
+
|
15
|
+
```liquid
|
16
|
+
{% assign arr = 'a,b,c' | split: ','}
|
17
|
+
{% for el in arr %}
|
18
|
+
{% graphql g = 'my/graphql', el: el %}
|
19
|
+
{% print el %}
|
20
|
+
{% endfor %}
|
21
|
+
|
22
|
+
```
|
23
|
+
|
24
|
+
:+1: Examples of **correct** code for this check:
|
25
|
+
|
26
|
+
```liquid
|
27
|
+
{% assign arr = 'a,b,c' | split: ','}
|
28
|
+
{% graphql g = 'my/graphql', arr: arr %}
|
29
|
+
```
|
30
|
+
|
31
|
+
## Check Options
|
32
|
+
|
33
|
+
The default configuration for this check is the following:
|
34
|
+
|
35
|
+
```yaml
|
36
|
+
GraphqlInForLoop:
|
37
|
+
enabled: true
|
38
|
+
```
|
39
|
+
|
40
|
+
## When Not To Use It
|
41
|
+
|
42
|
+
In the perfect world, there should be no cases where disabling this rule is needed - platformOS most likely already has a way to solve a problem without using GraphQL query / mutation in the for loop.
|
43
|
+
|
44
|
+
## Version
|
45
|
+
|
46
|
+
This check has been introduced in PlatformOS Check 0.4.9.
|
47
|
+
|
48
|
+
## Resources
|
49
|
+
|
50
|
+
- [Rule Source][codesource]
|
51
|
+
- [Documentation Source][docsource]
|
52
|
+
- [platformOS - loading related records][related_records]
|
53
|
+
|
54
|
+
[codesource]: /lib/platformos_check/checks/graphql_in_for_loop.rb
|
55
|
+
[docsource]: /docs/checks/graphql_in_for_loop.md
|
56
|
+
[related_records]: https://documentation.platformos.com/developer-guide/records/loading-related-records
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Reports usage of `include` tag inside `render` (`IncludeInRender`)
|
2
|
+
|
3
|
+
Runtime error is used when `include` tag is used inside `render` tag.
|
4
|
+
|
5
|
+
## Check Details
|
6
|
+
|
7
|
+
This check is aimed at eliminating the use of `include` tags `render` tag.
|
8
|
+
|
9
|
+
:-1: Examples of **incorrect** code for this check:
|
10
|
+
|
11
|
+
```liquid
|
12
|
+
{% liquid
|
13
|
+
# app/views/pages/index.liquid
|
14
|
+
render 'foo'
|
15
|
+
%}
|
16
|
+
```liquid
|
17
|
+
{% liquid
|
18
|
+
# app/views/partials/foo.liquid
|
19
|
+
include 'bar'
|
20
|
+
%}
|
21
|
+
```
|
22
|
+
|
23
|
+
:+1: Examples of **correct** code for this check:
|
24
|
+
|
25
|
+
```liquid
|
26
|
+
{% liquid
|
27
|
+
# app/views/pages/index.liquid
|
28
|
+
render 'foo'
|
29
|
+
%}
|
30
|
+
```liquid
|
31
|
+
{% liquid
|
32
|
+
# app/views/partials/foo.liquid
|
33
|
+
render 'bar'
|
34
|
+
%}
|
35
|
+
```
|
36
|
+
|
37
|
+
## Check Options
|
38
|
+
|
39
|
+
The default configuration for this check is the following:
|
40
|
+
|
41
|
+
```yaml
|
42
|
+
IncludeInRender:
|
43
|
+
enabled: true
|
44
|
+
```
|
45
|
+
|
46
|
+
## When Not To Use It
|
47
|
+
|
48
|
+
It is discouraged to disable this rule.
|
49
|
+
|
50
|
+
## Version
|
51
|
+
|
52
|
+
This check has been introduced in PlatformOS Check 0.4.9.
|
53
|
+
|
54
|
+
## Resources
|
55
|
+
|
56
|
+
- [Deprecated Tags Reference][deprecated]
|
57
|
+
- [Rule Source][codesource]
|
58
|
+
- [Documentation Source][docsource]
|
59
|
+
|
60
|
+
[deprecated]: https://documentation.platformos.com/api-reference/liquid/include
|
61
|
+
[codesource]: /lib/platformos_check/checks/convert_include_to_render.rb
|
62
|
+
[docsource]: /docs/checks/convert_include_to_render.md
|
@@ -2,13 +2,15 @@
|
|
2
2
|
|
3
3
|
module PlatformosCheck
|
4
4
|
class Analyzer
|
5
|
-
def initialize(platformos_app, checks = Check.all.map(&:new), auto_correct = false)
|
5
|
+
def initialize(platformos_app, checks = Check.all.map(&:new), auto_correct = false, store_warnings = false)
|
6
6
|
@platformos_app = platformos_app
|
7
7
|
@auto_correct = auto_correct
|
8
|
+
@store_warnings = store_warnings
|
8
9
|
|
9
10
|
@liquid_checks = Checks.new
|
10
11
|
@yaml_checks = Checks.new
|
11
12
|
@html_checks = Checks.new
|
13
|
+
@warnings = {}
|
12
14
|
|
13
15
|
checks.each do |check|
|
14
16
|
check.platformos_app = @platformos_app
|
@@ -30,6 +32,8 @@ module PlatformosCheck
|
|
30
32
|
@html_checks.flat_map(&:offenses)
|
31
33
|
end
|
32
34
|
|
35
|
+
attr_reader :warnings
|
36
|
+
|
33
37
|
def yaml_file_count
|
34
38
|
@yaml_file_count ||= @platformos_app.yaml.size
|
35
39
|
end
|
@@ -54,6 +58,7 @@ module PlatformosCheck
|
|
54
58
|
yield(liquid_file.relative_path.to_s, i, total_file_count) if block_given?
|
55
59
|
liquid_visitor.visit_liquid_file(liquid_file)
|
56
60
|
html_visitor.visit_liquid_file(liquid_file)
|
61
|
+
@warnings[liquid_file.path] = liquid_file.warnings if @store_warnings && !liquid_file.warnings&.empty?
|
57
62
|
end
|
58
63
|
end
|
59
64
|
|
@@ -3,15 +3,54 @@
|
|
3
3
|
module PlatformosCheck
|
4
4
|
# Recommends replacing `include` for `render`
|
5
5
|
class ConvertIncludeToRender < LiquidCheck
|
6
|
+
RENDER_INCOMPATIBLE_TAGS = %w[break include].freeze
|
7
|
+
|
6
8
|
severity :suggestion
|
7
9
|
category :liquid
|
8
10
|
doc docs_url(__FILE__)
|
9
11
|
|
12
|
+
def initialize
|
13
|
+
@processed_files = {}
|
14
|
+
end
|
15
|
+
|
10
16
|
def on_include(node)
|
17
|
+
return if allowed_usecase?(node)
|
18
|
+
|
11
19
|
add_offense("`include` is deprecated - convert it to `render`", node:) do |corrector|
|
12
|
-
|
13
|
-
|
20
|
+
match = node.markup.match(/(?<include>include\s*)/)
|
21
|
+
corrector.replace(node, node.markup.sub(match[:include], 'render '), node.start_index...node.end_index)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def allowed_usecase?(node)
|
28
|
+
return true if name_is_variable?(node)
|
29
|
+
return true if include_node_contains_render_incompatible_tag?(root_node_from_include(node.value.template_name_expr))
|
30
|
+
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def name_is_variable?(node)
|
35
|
+
!node.value.template_name_expr.is_a?(String)
|
36
|
+
end
|
37
|
+
|
38
|
+
def include_node_contains_render_incompatible_tag?(node)
|
39
|
+
return false if node.nil?
|
40
|
+
|
41
|
+
node.nodelist.any? do |n|
|
42
|
+
if RENDER_INCOMPATIBLE_TAGS.include?(n.respond_to?(:tag_name) && n.tag_name)
|
43
|
+
true
|
44
|
+
elsif n.respond_to?(:nodelist) && n.nodelist
|
45
|
+
include_node_contains_render_incompatible_tag?(n)
|
46
|
+
else
|
47
|
+
false
|
48
|
+
end
|
14
49
|
end
|
15
50
|
end
|
51
|
+
|
52
|
+
def root_node_from_include(path)
|
53
|
+
@platformos_app.grouped_files[PlatformosCheck::PartialFile][path]&.parse&.root
|
54
|
+
end
|
16
55
|
end
|
17
56
|
end
|
@@ -6,11 +6,13 @@ module PlatformosCheck
|
|
6
6
|
categories :html
|
7
7
|
doc docs_url(__FILE__)
|
8
8
|
|
9
|
+
VALID_ACTION_START = ['/', '{%', '{{', '#', 'http'].freeze
|
10
|
+
|
9
11
|
def on_form(node)
|
10
12
|
action = node.attributes["action"]&.strip
|
11
13
|
return if action.nil?
|
12
14
|
return if action.empty?
|
13
|
-
return if action.start_with?(
|
15
|
+
return if action.start_with?(*VALID_ACTION_START)
|
14
16
|
|
15
17
|
add_offense("Use action=\"/#{action}\" (start with /) to ensure the form can be submitted multiple times in case of validation errors", node:)
|
16
18
|
end
|
@@ -9,6 +9,9 @@ module PlatformosCheck
|
|
9
9
|
AUTHENTICITY_TOKEN_VALUE = /\A\s*{{\s*context\.authenticity_token\s*}}\s*\z/
|
10
10
|
|
11
11
|
def on_form(node)
|
12
|
+
return if method_is_get(node.attributes['method'])
|
13
|
+
return unless action_is_relative_url(node.attributes['action'])
|
14
|
+
|
12
15
|
authenticity_toke_inputs = node.children.select { |c| c.name == 'input' && c.attributes['name'] == 'authenticity_token' && c.attributes['value']&.match?(AUTHENTICITY_TOKEN_VALUE) }
|
13
16
|
return if authenticity_toke_inputs.size == 1
|
14
17
|
return add_offense('Duplicated authenticity_token inputs', node:) if authenticity_toke_inputs.size > 1
|
@@ -17,5 +20,22 @@ module PlatformosCheck
|
|
17
20
|
corrector.insert_after(node, "\n<input type=\"hidden\" name=\"authenticity_token\" value=\"{{ context.authenticity_token }}\">")
|
18
21
|
end
|
19
22
|
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def method_is_get(method)
|
27
|
+
return true if method.nil?
|
28
|
+
|
29
|
+
method = method.downcase.strip
|
30
|
+
return true if method == ''
|
31
|
+
|
32
|
+
method == 'get'
|
33
|
+
end
|
34
|
+
|
35
|
+
def action_is_relative_url(action)
|
36
|
+
return true if action.nil?
|
37
|
+
|
38
|
+
action.lstrip[0] == '/'
|
39
|
+
end
|
20
40
|
end
|
21
41
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
class GraphqlInForLoop < LiquidCheck
|
5
|
+
severity :suggestion
|
6
|
+
categories :liquid, :performance
|
7
|
+
doc docs_url(__FILE__)
|
8
|
+
|
9
|
+
PARTIAL_TAG = %i[render include]
|
10
|
+
OFFENSE_MSG = "Do not invoke GraphQL in a for loop"
|
11
|
+
|
12
|
+
class PartialInfo
|
13
|
+
attr_reader :node, :app_file
|
14
|
+
|
15
|
+
def initialize(node:, app_file:)
|
16
|
+
@node = node
|
17
|
+
@app_file = app_file
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.single_file(**_args)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@partials = []
|
27
|
+
@all_partials = Set.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_for(_node)
|
31
|
+
@in_for = true
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_graphql(node)
|
35
|
+
add_graphql_offense(node:, graphql_node: node) if should_report?
|
36
|
+
end
|
37
|
+
|
38
|
+
def after_for(_node)
|
39
|
+
@in_for = false
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_background(_node)
|
43
|
+
@in_background = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def after_background(_node)
|
47
|
+
@in_background = false
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_include(node)
|
51
|
+
return unless should_report?
|
52
|
+
|
53
|
+
add_partial(path: node.value.template_name_expr, node:)
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_render(node)
|
57
|
+
return unless should_report?
|
58
|
+
|
59
|
+
add_partial(path: node.value.template_name_expr, node:)
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_function(node)
|
63
|
+
return unless should_report?
|
64
|
+
|
65
|
+
add_partial(path: node.value.from, node:)
|
66
|
+
end
|
67
|
+
|
68
|
+
def on_end
|
69
|
+
while (partial_info = @partials.shift)
|
70
|
+
report_offense_on_graphql(LiquidNode.new(partial_info.app_file.parse.root, nil, partial_info.app_file), offense_node: partial_info.node)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
def add_partial(path:, node:)
|
77
|
+
return unless path.is_a?(String)
|
78
|
+
return if @all_partials.include?(path)
|
79
|
+
return if @platformos_app.grouped_files[PlatformosCheck::PartialFile][path].nil?
|
80
|
+
|
81
|
+
@all_partials << path
|
82
|
+
@partials << PartialInfo.new(node:, app_file: @platformos_app.grouped_files[PlatformosCheck::PartialFile][path])
|
83
|
+
end
|
84
|
+
|
85
|
+
def should_report?
|
86
|
+
@in_for && !@in_background
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_graphql_offense(node:, graphql_node:)
|
90
|
+
return add_offense(OFFENSE_MSG, node:) unless graphql_node.value.partial_name
|
91
|
+
|
92
|
+
partial_name = graphql_node.value.partial_name.is_a?(String) ? graphql_node.value.partial_name : "variable: #{graphql_node.value.partial_name.name}"
|
93
|
+
add_offense("#{OFFENSE_MSG} (#{partial_name})", node:)
|
94
|
+
end
|
95
|
+
|
96
|
+
def report_offense_on_graphql(node, offense_node:)
|
97
|
+
if node.type_name == :graphql
|
98
|
+
add_graphql_offense(node: offense_node, graphql_node: node)
|
99
|
+
elsif PARTIAL_TAG.include?(node.type_name)
|
100
|
+
add_partial(path: node.value.template_name_expr, node: offense_node)
|
101
|
+
elsif node.type_name == :function
|
102
|
+
add_partial(path: node.value.from, node: offense_node)
|
103
|
+
elsif node.children && !node.children.empty?
|
104
|
+
node.children.each { |c| report_offense_on_graphql(c, offense_node:) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -6,13 +6,17 @@ module PlatformosCheck
|
|
6
6
|
categories :html, :performance
|
7
7
|
doc docs_url(__FILE__)
|
8
8
|
|
9
|
-
ACCEPTED_LOADING_VALUES = %w[lazy eager]
|
9
|
+
ACCEPTED_LOADING_VALUES = Set.new(%w[lazy eager]).freeze
|
10
|
+
LOADING_DEFAULT_ATTRIBUTE = ' loading="eager"'
|
10
11
|
|
11
12
|
def on_img(node)
|
12
13
|
loading = node.attributes["loading"]&.downcase
|
13
14
|
return if ACCEPTED_LOADING_VALUES.include?(loading)
|
14
15
|
|
15
|
-
add_offense("Use loading=\"eager\" for images visible in the viewport on load and loading=\"lazy\" for others", node:)
|
16
|
+
add_offense("Use loading=\"eager\" for images visible in the viewport on load and loading=\"lazy\" for others", node:) do |corrector|
|
17
|
+
start_pos = node.start_index + node.markup.index('>')
|
18
|
+
corrector.insert_after(node, LOADING_DEFAULT_ATTRIBUTE, start_pos...start_pos)
|
19
|
+
end
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
# Recommends replacing `include` for `render`
|
5
|
+
class IncludeInRender < LiquidCheck
|
6
|
+
severity :error
|
7
|
+
category :liquid
|
8
|
+
doc docs_url(__FILE__)
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@processed_files = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_render(node)
|
15
|
+
path = node.value.template_name_expr
|
16
|
+
return unless include_tag_in_render?(root_node_for_render(path))
|
17
|
+
|
18
|
+
add_offense("`render` context does not allow to use `include`, either remove all includes from `#{app_file_for_path(path).relative_path}` or change `render` to `include`", node:)
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def include_tag_in_render?(node)
|
24
|
+
return false if node.nil?
|
25
|
+
|
26
|
+
node.nodelist.any? do |n|
|
27
|
+
if n.respond_to?(:tag_name) && n.tag_name == 'include'
|
28
|
+
true
|
29
|
+
elsif n.respond_to?(:nodelist) && n.nodelist
|
30
|
+
include_tag_in_render?(n)
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def root_node_for_render(path)
|
38
|
+
app_file_for_path(path)&.parse&.root
|
39
|
+
end
|
40
|
+
|
41
|
+
def app_file_for_path(path)
|
42
|
+
@platformos_app.grouped_files[PlatformosCheck::PartialFile][path]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -42,7 +42,10 @@ module PlatformosCheck
|
|
42
42
|
|
43
43
|
def add_duplicated_key_offense(node)
|
44
44
|
node.value.duplicated_attrs.each do |duplicated_arg|
|
45
|
-
add_offense("Duplicated argument `#{duplicated_arg}`", node:)
|
45
|
+
add_offense("Duplicated argument `#{duplicated_arg}`", node:) do |corrector|
|
46
|
+
match = node.markup.match(/(?<attribute>,?\s*#{duplicated_arg}\s*:\s*#{Liquid::QuotedFragment})\s*/)
|
47
|
+
corrector.replace(node, node.markup.sub(match[:attribute], ''), node.start_index...node.end_index)
|
48
|
+
end
|
46
49
|
end
|
47
50
|
end
|
48
51
|
end
|
@@ -6,6 +6,9 @@ module PlatformosCheck
|
|
6
6
|
doc docs_url(__FILE__)
|
7
7
|
severity :error
|
8
8
|
|
9
|
+
NOTIFICATION_GLOBAL_OBJECTS = %w[data response form].freeze
|
10
|
+
FORM_GLOBAL_OBJECTS = %w[form form_builder].freeze
|
11
|
+
|
9
12
|
class TemplateInfo
|
10
13
|
def initialize(app_file: nil)
|
11
14
|
@all_variable_lookups = {}
|
@@ -19,7 +22,8 @@ module PlatformosCheck
|
|
19
22
|
attr_reader :all_assigns, :all_captures, :all_forloops, :app_file, :all_renders
|
20
23
|
|
21
24
|
def add_render(name:, node:)
|
22
|
-
@all_renders[name]
|
25
|
+
@all_renders[name] ||= []
|
26
|
+
@all_renders[name] << node
|
23
27
|
end
|
24
28
|
|
25
29
|
def add_variable_lookup(name:, node:)
|
@@ -39,8 +43,10 @@ module PlatformosCheck
|
|
39
43
|
end
|
40
44
|
|
41
45
|
def each_partial
|
42
|
-
@all_renders.each do |(name,
|
43
|
-
|
46
|
+
@all_renders.each do |(name, nodes)|
|
47
|
+
nodes.each do |node|
|
48
|
+
yield [name, node]
|
49
|
+
end
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
@@ -56,14 +62,17 @@ module PlatformosCheck
|
|
56
62
|
yield [key, info]
|
57
63
|
end
|
58
64
|
end
|
65
|
+
|
66
|
+
def first_declaration(name)
|
67
|
+
[all_assigns[name], all_captures[name]].compact.sort_by(&:line_number).first
|
68
|
+
end
|
59
69
|
end
|
60
70
|
|
61
71
|
def self.single_file(**_args)
|
62
72
|
true
|
63
73
|
end
|
64
74
|
|
65
|
-
def initialize
|
66
|
-
@config_type = config_type
|
75
|
+
def initialize
|
67
76
|
@files = {}
|
68
77
|
end
|
69
78
|
|
@@ -72,15 +81,15 @@ module PlatformosCheck
|
|
72
81
|
end
|
73
82
|
|
74
83
|
def on_assign(node)
|
75
|
-
@files[node.app_file.name].all_assigns[node.value.to]
|
84
|
+
@files[node.app_file.name].all_assigns[node.value.to] ||= node
|
76
85
|
end
|
77
86
|
|
78
87
|
def on_capture(node)
|
79
|
-
@files[node.app_file.name].all_captures[node.value.instance_variable_get(:@to)]
|
88
|
+
@files[node.app_file.name].all_captures[node.value.instance_variable_get(:@to)] ||= node
|
80
89
|
end
|
81
90
|
|
82
91
|
def on_parse_json(node)
|
83
|
-
@files[node.app_file.name].all_captures[node.value.to]
|
92
|
+
@files[node.app_file.name].all_captures[node.value.to] ||= node
|
84
93
|
end
|
85
94
|
|
86
95
|
def on_for(node)
|
@@ -102,7 +111,7 @@ module PlatformosCheck
|
|
102
111
|
end
|
103
112
|
|
104
113
|
def on_function(node)
|
105
|
-
@files[node.app_file.name].all_assigns[node.value.to]
|
114
|
+
@files[node.app_file.name].all_assigns[node.value.to] ||= node
|
106
115
|
|
107
116
|
return unless node.value.from.is_a?(String)
|
108
117
|
|
@@ -113,13 +122,13 @@ module PlatformosCheck
|
|
113
122
|
end
|
114
123
|
|
115
124
|
def on_graphql(node)
|
116
|
-
@files[node.app_file.name].all_assigns[node.value.to]
|
125
|
+
@files[node.app_file.name].all_assigns[node.value.to] ||= node
|
117
126
|
end
|
118
127
|
|
119
128
|
def on_background(node)
|
120
129
|
return unless node.value.partial_syntax
|
121
130
|
|
122
|
-
@files[node.app_file.name].all_assigns[node.value.to]
|
131
|
+
@files[node.app_file.name].all_assigns[node.value.to] ||= node
|
123
132
|
|
124
133
|
return unless node.value.partial_name.is_a?(String)
|
125
134
|
|
@@ -155,10 +164,10 @@ module PlatformosCheck
|
|
155
164
|
each_template do |(_name, info)|
|
156
165
|
if info.app_file.notification?
|
157
166
|
# NOTE: `data` comes from graphql for notifications
|
158
|
-
check_object(info, all_global_objects +
|
167
|
+
check_object(info, all_global_objects + NOTIFICATION_GLOBAL_OBJECTS)
|
159
168
|
elsif info.app_file.form?
|
160
169
|
# NOTE: `data` comes from graphql for notifications
|
161
|
-
check_object(info, all_global_objects +
|
170
|
+
check_object(info, all_global_objects + FORM_GLOBAL_OBJECTS)
|
162
171
|
else
|
163
172
|
check_object(info, all_global_objects)
|
164
173
|
end
|
@@ -167,42 +176,37 @@ module PlatformosCheck
|
|
167
176
|
|
168
177
|
private
|
169
178
|
|
170
|
-
attr_reader :config_type
|
171
|
-
|
172
179
|
def each_template
|
173
180
|
@files.each do |(name, info)|
|
174
181
|
yield [name, info]
|
175
182
|
end
|
176
183
|
end
|
177
184
|
|
178
|
-
def check_object(info, all_global_objects, render_node = nil,
|
185
|
+
def check_object(info, all_global_objects, render_node = nil, level = 0)
|
179
186
|
return if level > 1
|
180
187
|
|
181
188
|
check_undefined(info, all_global_objects, render_node) unless info.app_file.partial? && render_node.nil? # ||
|
182
189
|
|
183
190
|
info.each_partial do |(partial_name, node)|
|
184
|
-
next
|
191
|
+
next unless @files[partial_name] # NOTE: undefined partial
|
185
192
|
|
186
193
|
partial_info = @files[partial_name]
|
187
|
-
|
188
|
-
next unless partial_info # NOTE: undefined partial
|
189
|
-
|
190
194
|
partial_variables = node.value.attributes.keys +
|
191
195
|
[node.value.instance_variable_get(:@alias_name)]
|
192
|
-
|
193
|
-
check_object(partial_info, all_global_objects + partial_variables, node,
|
196
|
+
|
197
|
+
check_object(partial_info, all_global_objects + partial_variables, node, level + 1)
|
194
198
|
end
|
195
199
|
end
|
196
200
|
|
197
201
|
def check_undefined(info, all_global_objects, render_node)
|
198
|
-
all_variables = info.all_variables
|
199
202
|
potentially_unused_variables = render_node.value.attributes.keys if render_node
|
203
|
+
missing_arguments = []
|
200
204
|
info.each_variable_lookup(!!render_node) do |(key, node)|
|
201
205
|
name, line_number = key
|
202
206
|
|
203
207
|
potentially_unused_variables&.delete(name)
|
204
208
|
|
205
|
-
next if all_variables.include?(name)
|
209
|
+
next if info.all_variables.include?(name) && variable_declared_before_used?(name, info, line_number)
|
206
210
|
next if all_global_objects.include?(name)
|
207
211
|
|
208
212
|
node = node.parent
|
@@ -211,7 +215,7 @@ module PlatformosCheck
|
|
211
215
|
next if node.variable? && node.filters.any? { |(filter_name)| filter_name == "default" }
|
212
216
|
|
213
217
|
if render_node
|
214
|
-
|
218
|
+
missing_arguments << name
|
215
219
|
elsif !info.app_file.partial?
|
216
220
|
add_offense("Undefined object `#{name}`", node:, line_number:)
|
217
221
|
end
|
@@ -219,8 +223,33 @@ module PlatformosCheck
|
|
219
223
|
|
220
224
|
potentially_unused_variables -= render_node.value.internal_attributes if render_node && render_node.value.respond_to?(:internal_attributes)
|
221
225
|
potentially_unused_variables&.each do |name|
|
222
|
-
add_offense("Unused argument `#{name}`", node: render_node)
|
226
|
+
add_offense("Unused argument `#{name}`", node: render_node) do |corrector|
|
227
|
+
match = render_node.markup.match(/(?<attribute>,?\s*#{name}\s*:\s*#{Liquid::QuotedFragment})\s*/)
|
228
|
+
|
229
|
+
corrector.replace(render_node, render_node.markup.sub(match[:attribute], ''), render_node.start_index...render_node.end_index)
|
230
|
+
end
|
223
231
|
end
|
232
|
+
|
233
|
+
return if missing_arguments.empty?
|
234
|
+
|
235
|
+
add_offense("Missing arguments: #{missing_arguments.map { |name| "`#{name}`" }.join(', ')}", node: render_node) do |corrector|
|
236
|
+
new_attributes = ''
|
237
|
+
missing_arguments.each do |name|
|
238
|
+
new_attributes += ", #{name}: "
|
239
|
+
new_attributes += @files[render_node.app_file.name].all_assigns.key?(name) ? name : 'null'
|
240
|
+
end
|
241
|
+
|
242
|
+
start_pos = render_node.end_index
|
243
|
+
start_pos -= 1 while start_pos > 0 && render_node.source[start_pos - 1] == ' '
|
244
|
+
corrector.replace(render_node, new_attributes, start_pos...start_pos)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def variable_declared_before_used?(name, info, line_number_when_used)
|
249
|
+
declaration = info.first_declaration(name)
|
250
|
+
return true if declaration.nil?
|
251
|
+
|
252
|
+
declaration.line_number <= line_number_when_used
|
224
253
|
end
|
225
254
|
end
|
226
255
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PlatformosCheck
|
4
|
-
# Recommends using {% liquid ... %} if 5 or more consecutive {% ... %} are found.
|
5
4
|
class UnreachableCode < LiquidCheck
|
6
5
|
severity :error
|
7
6
|
category :liquid
|
@@ -9,7 +8,7 @@ module PlatformosCheck
|
|
9
8
|
|
10
9
|
FLOW_COMMAND = %i[break continue return]
|
11
10
|
CONDITION_TYPES = Set.new(%i[condition else_condition])
|
12
|
-
INCLUDE_FLOW_COMMAND = %w[break]
|
11
|
+
INCLUDE_FLOW_COMMAND = %w[break].freeze
|
13
12
|
|
14
13
|
def on_document(node)
|
15
14
|
@processed_files = {}
|
@@ -95,16 +94,16 @@ module PlatformosCheck
|
|
95
94
|
@processed_files[path]
|
96
95
|
end
|
97
96
|
|
98
|
-
def include_node_contains_flow_command?(
|
99
|
-
return false if
|
97
|
+
def include_node_contains_flow_command?(node)
|
98
|
+
return false if node.nil?
|
100
99
|
|
101
|
-
|
102
|
-
if INCLUDE_FLOW_COMMAND.include?(
|
100
|
+
node.nodelist.any? do |n|
|
101
|
+
if INCLUDE_FLOW_COMMAND.include?(n.respond_to?(:tag_name) && n.tag_name)
|
103
102
|
true
|
104
|
-
elsif
|
105
|
-
include_node_contains_flow_command?(
|
106
|
-
elsif
|
107
|
-
evaluate_include(
|
103
|
+
elsif n.respond_to?(:nodelist) && n.nodelist
|
104
|
+
include_node_contains_flow_command?(n)
|
105
|
+
elsif n.respond_to?(:tag_name) && n.tag_name == 'include' && n.template_name_expr.is_a?(String)
|
106
|
+
evaluate_include(n.template_name_expr)
|
108
107
|
else
|
109
108
|
false
|
110
109
|
end
|
@@ -7,6 +7,13 @@ module PlatformosCheck
|
|
7
7
|
category :liquid
|
8
8
|
doc docs_url(__FILE__)
|
9
9
|
|
10
|
+
TAGS_FOR_AUTO_VARIABLE_PREPEND = Set.new(%i[graphql function background]).freeze
|
11
|
+
FILTERS_THAT_MODIFY_OBJECT = Set.new(%w[array_add add_to_array
|
12
|
+
prepend_to_array array_prepend
|
13
|
+
assign_to_hash_key hash_add_key add_hash_key
|
14
|
+
remove_hash_key hash_delete_key delete_hash_key]).freeze
|
15
|
+
PREPEND_CHARACTER = '_'
|
16
|
+
|
10
17
|
class TemplateInfo < Struct.new(:used_assigns, :assign_nodes, :includes)
|
11
18
|
def collect_used_assigns(templates, visited = Set.new)
|
12
19
|
collected = used_assigns
|
@@ -34,7 +41,8 @@ module PlatformosCheck
|
|
34
41
|
end
|
35
42
|
|
36
43
|
def on_assign(node)
|
37
|
-
return if
|
44
|
+
return if ignore_prepended?(node)
|
45
|
+
return if node.value.from.filters.any? { |filter_name, *_arguments| FILTERS_THAT_MODIFY_OBJECT.include?(filter_name) }
|
38
46
|
|
39
47
|
@templates[node.app_file.name].assign_nodes[node.value.to] = node
|
40
48
|
end
|
@@ -44,13 +52,21 @@ module PlatformosCheck
|
|
44
52
|
end
|
45
53
|
|
46
54
|
def on_function(node)
|
47
|
-
return if
|
55
|
+
return if ignore_prepended?(node)
|
48
56
|
|
49
57
|
@templates[node.app_file.name].assign_nodes[node.value.to] = node
|
50
58
|
end
|
51
59
|
|
52
60
|
def on_graphql(node)
|
53
|
-
return if
|
61
|
+
return if node.value.to.nil?
|
62
|
+
return if ignore_prepended?(node)
|
63
|
+
|
64
|
+
@templates[node.app_file.name].assign_nodes[node.value.to] = node
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_background(node)
|
68
|
+
return if node.value.to.nil?
|
69
|
+
return if ignore_prepended?(node)
|
54
70
|
|
55
71
|
@templates[node.app_file.name].assign_nodes[node.value.to] = node
|
56
72
|
end
|
@@ -77,25 +93,8 @@ module PlatformosCheck
|
|
77
93
|
next if used.include?(name)
|
78
94
|
|
79
95
|
add_offense("`#{name}` is never used", node:) do |corrector|
|
80
|
-
|
81
|
-
|
82
|
-
offset = node.markup.match(/^graphql\s+/)[0].size
|
83
|
-
|
84
|
-
corrector.insert_before(
|
85
|
-
node,
|
86
|
-
'_',
|
87
|
-
(node.start_index + offset)...(node.start_index + offset)
|
88
|
-
)
|
89
|
-
when :function
|
90
|
-
offset = node.markup.match(/^function\s+/)[0].size
|
91
|
-
|
92
|
-
corrector.insert_before(
|
93
|
-
node,
|
94
|
-
'_',
|
95
|
-
(node.start_index + offset)...(node.start_index + offset)
|
96
|
-
)
|
97
|
-
when :parse_json
|
98
|
-
# noop
|
96
|
+
if TAGS_FOR_AUTO_VARIABLE_PREPEND.include?(node.type_name)
|
97
|
+
prepend_variable(node, corrector)
|
99
98
|
else
|
100
99
|
corrector.remove(node)
|
101
100
|
end
|
@@ -106,8 +105,18 @@ module PlatformosCheck
|
|
106
105
|
|
107
106
|
private
|
108
107
|
|
109
|
-
def
|
110
|
-
node.value.to.start_with?(
|
108
|
+
def ignore_prepended?(node)
|
109
|
+
node.value.to.start_with?(PREPEND_CHARACTER)
|
110
|
+
end
|
111
|
+
|
112
|
+
def prepend_variable(node, corrector)
|
113
|
+
offset = node.markup.match(/^#{node.type_name}\s+/)[0].size
|
114
|
+
|
115
|
+
corrector.insert_before(
|
116
|
+
node,
|
117
|
+
PREPEND_CHARACTER,
|
118
|
+
(node.start_index + offset)...(node.start_index + offset)
|
119
|
+
)
|
111
120
|
end
|
112
121
|
end
|
113
122
|
end
|
data/lib/platformos_check/cli.rb
CHANGED
@@ -188,7 +188,7 @@ module PlatformosCheck
|
|
188
188
|
def check(out_stream = STDOUT)
|
189
189
|
update_docs
|
190
190
|
|
191
|
-
warn "Checking #{@config.root}
|
191
|
+
warn "Checking #{@config.root}:"
|
192
192
|
storage = PlatformosCheck::FileSystemStorage.new(@config.root, ignored_patterns: @config.ignored_patterns)
|
193
193
|
raise Abort, "No platformos_app files found." if storage.platformos_app.all.empty?
|
194
194
|
|
@@ -5,6 +5,7 @@ module PlatformosCheck
|
|
5
5
|
class Graphql < Base
|
6
6
|
QUERY_NAME_SYNTAX = /(#{Liquid::VariableSignature}+)\s*=\s*(.*)\s*/om
|
7
7
|
INLINE_SYNTAX = /(#{Liquid::QuotedFragment}+)(\s*(#{Liquid::QuotedFragment}+))?/o
|
8
|
+
INLINE_SYNTAX_WITHOUT_RESULT_VARIABLE = /\A([\w\-\.\[\]])+\s*:\s*/om
|
8
9
|
CLOSE_TAG_SYNTAX = /\A(.*)(?-mix:\{%-?)\s*(\w+)\s*(.*)?(?-mix:%\})\z/m # based on Liquid::Raw::FullTokenPossiblyInvalid
|
9
10
|
|
10
11
|
attr_reader :to, :from, :inline_query, :value_expr, :partial_name, :attributes_expr, :attributes
|
@@ -28,6 +29,8 @@ module PlatformosCheck
|
|
28
29
|
@partial_name = value_expr
|
29
30
|
@from = Liquid::Variable.new(after_assign_markup.join('|'), options)
|
30
31
|
elsif INLINE_SYNTAX.match?(markup)
|
32
|
+
raise Liquid::SyntaxError, 'Invalid syntax for inline graphql tag - missing result name. Valid syntax: graphql result, arg1: var1, ...' if markup.match?(INLINE_SYNTAX_WITHOUT_RESULT_VARIABLE)
|
33
|
+
|
31
34
|
@inline_query = true
|
32
35
|
parse_markup(tag_name, markup)
|
33
36
|
@attributes = attributes_expr.keys
|
@@ -53,6 +53,8 @@ module PlatformosCheck
|
|
53
53
|
register_tag('background', Background)
|
54
54
|
register_tag('content_for', ContentFor)
|
55
55
|
register_tag('session', Session)
|
56
|
+
register_tag('context', Context)
|
57
|
+
register_tag('context_rc', Context)
|
56
58
|
register_tag('sign_in', SignIn)
|
57
59
|
register_tag('yield', Yield)
|
58
60
|
register_tag('graphql', Graphql)
|
data/lib/platformos_check.rb
CHANGED
@@ -47,6 +47,7 @@ require_relative "platformos_check/tags/base"
|
|
47
47
|
require_relative "platformos_check/tags/base_block"
|
48
48
|
require_relative "platformos_check/tags/background"
|
49
49
|
require_relative "platformos_check/tags/cache"
|
50
|
+
require_relative "platformos_check/tags/context"
|
50
51
|
require_relative "platformos_check/tags/export"
|
51
52
|
require_relative "platformos_check/tags/form"
|
52
53
|
require_relative "platformos_check/tags/function"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: platformos-check
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Bliszczyk
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2024-01-10 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: graphql
|
@@ -119,9 +119,11 @@ files:
|
|
119
119
|
- docs/checks/deprecated_filter.md
|
120
120
|
- docs/checks/form_action.md
|
121
121
|
- docs/checks/form_authenticity_token.md
|
122
|
+
- docs/checks/graphql_in_for_loop.md
|
122
123
|
- docs/checks/html_parsing_error.md
|
123
124
|
- docs/checks/img_lazy_loading.md
|
124
125
|
- docs/checks/img_width_and_height.md
|
126
|
+
- docs/checks/include_in_render.md
|
125
127
|
- docs/checks/invalid_args.md
|
126
128
|
- docs/checks/liquid_tag.md
|
127
129
|
- docs/checks/missing_enable_comment.md
|
@@ -164,9 +166,11 @@ files:
|
|
164
166
|
- lib/platformos_check/checks/deprecated_filter.rb
|
165
167
|
- lib/platformos_check/checks/form_action.rb
|
166
168
|
- lib/platformos_check/checks/form_authenticity_token.rb
|
169
|
+
- lib/platformos_check/checks/graphql_in_for_loop.rb
|
167
170
|
- lib/platformos_check/checks/html_parsing_error.rb
|
168
171
|
- lib/platformos_check/checks/img_lazy_loading.rb
|
169
172
|
- lib/platformos_check/checks/img_width_and_height.rb
|
173
|
+
- lib/platformos_check/checks/include_in_render.rb
|
170
174
|
- lib/platformos_check/checks/invalid_args.rb
|
171
175
|
- lib/platformos_check/checks/liquid_tag.rb
|
172
176
|
- lib/platformos_check/checks/missing_enable_comment.rb
|
@@ -316,6 +320,7 @@ files:
|
|
316
320
|
- lib/platformos_check/tags/base_block.rb
|
317
321
|
- lib/platformos_check/tags/base_tag_methods.rb
|
318
322
|
- lib/platformos_check/tags/cache.rb
|
323
|
+
- lib/platformos_check/tags/context.rb
|
319
324
|
- lib/platformos_check/tags/export.rb
|
320
325
|
- lib/platformos_check/tags/form.rb
|
321
326
|
- lib/platformos_check/tags/function.rb
|