erb_lint 0.0.4 → 0.0.5
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 +4 -4
- data/lib/erb_lint/linters/deprecated_classes.rb +40 -117
- data/lib/erb_lint/linters/erb_safety.rb +35 -0
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a211bea7ec247bcf1d79ede8f37db1b6b7d1b8c
|
4
|
+
data.tar.gz: 362ca7b4ffb1f184818889166cb572198c1def7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45c2ff7d03c2ec43e37562887ad9cdc9f396b0084bf290036a848aa820fed661a0f99a49d120be62100aa15c640fa4374f5937c9accb0066a06028632ac56e8f
|
7
|
+
data.tar.gz: 16279ef10a1f004fe97309dde027de0bf244ac0a9e047b9a0ff04be73d5d9d71494e1e2ab08d584b9123ee96f35a05525ff7b8ff09d005d3f75f000c009eacbf
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'better_html'
|
4
|
+
|
3
5
|
module ERBLint
|
4
6
|
module Linters
|
5
7
|
# Checks for deprecated classes in the start tags of HTML elements.
|
@@ -22,144 +24,65 @@ module ERBLint
|
|
22
24
|
@addendum = config.fetch('addendum', '')
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
def lint_lines(lines)
|
27
|
+
def lint_file(file_content)
|
28
28
|
errors = []
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
errors.push(*generate_errors(class_name, index + 1))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
29
|
+
iterator = build_iterator(file_content)
|
30
|
+
each_class_name_with_line(iterator) do |class_name, line|
|
31
|
+
errors.push(*generate_errors(class_name, line))
|
32
|
+
end
|
33
|
+
each_texthtml_script(iterator) do |text_node|
|
34
|
+
errors.push(*lint_file(text_node.content))
|
39
35
|
end
|
40
36
|
errors
|
41
37
|
end
|
42
38
|
|
43
39
|
private
|
44
40
|
|
45
|
-
def
|
46
|
-
|
47
|
-
suggestion = " #{violated_rule[:suggestion]}".rstrip
|
48
|
-
message = "Deprecated class `%s` detected matching the pattern `%s`.%s #{@addendum}".strip
|
49
|
-
{
|
50
|
-
line: line_number,
|
51
|
-
message: format(message, class_name, violated_rule[:class_expr], suggestion)
|
52
|
-
}
|
53
|
-
end
|
41
|
+
def build_iterator(file_content)
|
42
|
+
BetterHtml::NodeIterator.new(file_content, template_language: :html)
|
54
43
|
end
|
55
44
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
45
|
+
def each_class_name_with_line(iterator)
|
46
|
+
each_element_with_index(iterator) do |element, _index|
|
47
|
+
klass = element.find_attr('class')
|
48
|
+
next unless klass
|
49
|
+
klass.value_without_quotes.split(' ').each do |class_name|
|
50
|
+
yield class_name, klass.name_parts.first.location.line
|
51
|
+
end
|
59
52
|
end
|
60
53
|
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Provides methods and classes for finding HTML start tags and their attributes.
|
64
|
-
module StartTagHelper
|
65
|
-
# These patterns cover a superset of the W3 HTML5 specification.
|
66
|
-
# Additional cases not included in the spec include those that are still rendered by some browsers.
|
67
|
-
|
68
|
-
# Attribute Patterns
|
69
|
-
# https://www.w3.org/TR/html5/syntax.html#syntax-attributes
|
70
|
-
|
71
|
-
# attribute names must be non empty and can't contain a certain set of special characters
|
72
|
-
ATTRIBUTE_NAME_PATTERN = %r{[^\s"'>\/=]+}
|
73
|
-
|
74
|
-
ATTRIBUTE_VALUE_PATTERN = %r{
|
75
|
-
"([^"]*)" | # double-quoted value
|
76
|
-
'([^']*)' | # single-quoted value
|
77
|
-
([^\s"'=<>`]+) # unquoted non-empty value without special characters
|
78
|
-
}x
|
79
|
-
|
80
|
-
# attributes can be empty or have an attribute value
|
81
|
-
ATTRIBUTE_PATTERN = %r{
|
82
|
-
#{ATTRIBUTE_NAME_PATTERN} # attribute name
|
83
|
-
(
|
84
|
-
\s*=\s* # any whitespace around equals sign
|
85
|
-
(#{ATTRIBUTE_VALUE_PATTERN}) # attribute value
|
86
|
-
)? # attributes can be empty or have an assignemnt.
|
87
|
-
}x
|
88
|
-
|
89
|
-
# Start tag Patterns
|
90
|
-
# https://www.w3.org/TR/html5/syntax.html#syntax-start-tag
|
91
|
-
|
92
|
-
TAG_NAME_PATTERN = /[A-Za-z0-9]+/ # maybe add _ < ? etc later since it gets interpreted by some browsers
|
93
54
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
(
|
98
|
-
\s+ # required whitespace between tag name and first attribute and between attributes
|
99
|
-
#{ATTRIBUTE_PATTERN} # attributes
|
100
|
-
)*
|
101
|
-
)? # having an attribute block is optional
|
102
|
-
\/?> # void or foreign elements can have a slash before tag close
|
103
|
-
}x
|
104
|
-
|
105
|
-
# Represents and provides an interface for a start tag found in the HTML.
|
106
|
-
class StartTag
|
107
|
-
attr_accessor :tag_name, :attributes
|
108
|
-
|
109
|
-
def initialize(tag_name, attributes)
|
110
|
-
@tag_name = tag_name
|
111
|
-
@attributes = attributes
|
55
|
+
def each_element_with_index(iterator)
|
56
|
+
iterator.nodes.each_with_index do |node, index|
|
57
|
+
yield node, index if node.element?
|
112
58
|
end
|
113
59
|
end
|
114
60
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
@attribute_name = attribute_name
|
122
|
-
@value = value
|
123
|
-
end
|
61
|
+
def each_texthtml_script(iterator)
|
62
|
+
each_element_with_index(iterator) do |element, index|
|
63
|
+
type = element.find_attr('type')
|
64
|
+
next unless type
|
65
|
+
next unless 'text/html' == type.value_without_quotes
|
66
|
+
next_node = iterator.nodes[index + 1]
|
124
67
|
|
125
|
-
|
126
|
-
ATTR_NAME_CLASS_PATTERN.match(@attribute_name)
|
68
|
+
yield next_node if next_node&.text?
|
127
69
|
end
|
128
70
|
end
|
129
71
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
tag_name = start_tag_matching_group[1]
|
139
|
-
|
140
|
-
# attributes_string can be nil if there is no space after the tag name (and therefore no attributes).
|
141
|
-
attributes_string = start_tag_matching_group[2] || ''
|
142
|
-
|
143
|
-
attribute_list = attributes(attributes_string)
|
144
|
-
|
145
|
-
StartTag.new(tag_name, attribute_list)
|
146
|
-
end
|
72
|
+
def generate_errors(class_name, line_number)
|
73
|
+
violated_rules(class_name).map do |violated_rule|
|
74
|
+
suggestion = " #{violated_rule[:suggestion]}".rstrip
|
75
|
+
message = "Deprecated class `%s` detected matching the pattern `%s`.%s #{@addendum}".strip
|
76
|
+
{
|
77
|
+
line: line_number,
|
78
|
+
message: format(message, class_name, violated_rule[:class_expr], suggestion)
|
79
|
+
}
|
147
80
|
end
|
81
|
+
end
|
148
82
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
attributes_string.scan(/(#{ATTRIBUTE_PATTERN})/).map do |attribute_matching_group|
|
153
|
-
entire_string = attribute_matching_group[0]
|
154
|
-
value_with_equal_sign = attribute_matching_group[1] || '' # This can be nil if attribute is empty
|
155
|
-
name = entire_string.sub(value_with_equal_sign, '')
|
156
|
-
|
157
|
-
# The 3 captures [3..5] are the possibilities specified in ATTRIBUTE_VALUE_PATTERN
|
158
|
-
possible_value_formats = attribute_matching_group[3..5]
|
159
|
-
value = possible_value_formats.reduce { |a, e| a.nil? ? e : a }
|
160
|
-
|
161
|
-
Attribute.new(name, value)
|
162
|
-
end
|
83
|
+
def violated_rules(class_name)
|
84
|
+
@deprecated_ruleset.select do |deprecated_rule|
|
85
|
+
/\A#{deprecated_rule[:class_expr]}\z/.match(class_name)
|
163
86
|
end
|
164
87
|
end
|
165
88
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'better_html'
|
4
|
+
require 'better_html/test_helper/safe_erb_tester'
|
5
|
+
|
6
|
+
module ERBLint
|
7
|
+
module Linters
|
8
|
+
# Detect unsafe ruby interpolations into javascript.
|
9
|
+
class ErbSafety < Linter
|
10
|
+
include LinterRegistry
|
11
|
+
include BetterHtml::TestHelper::SafeErbTester
|
12
|
+
|
13
|
+
def initialize(config)
|
14
|
+
end
|
15
|
+
|
16
|
+
def lint_file(file_content)
|
17
|
+
errors = []
|
18
|
+
tester = Tester.new(file_content)
|
19
|
+
tester.errors.each do |error|
|
20
|
+
errors << format_error(error)
|
21
|
+
end
|
22
|
+
errors
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def format_error(error)
|
28
|
+
{
|
29
|
+
line: error.token.location.line,
|
30
|
+
message: error.message
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erb_lint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Chan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-10-
|
11
|
+
date: 2017-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: html_tokenizer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: better_html
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.0.5
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rspec
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -49,6 +77,7 @@ files:
|
|
49
77
|
- lib/erb_lint/linter.rb
|
50
78
|
- lib/erb_lint/linter_registry.rb
|
51
79
|
- lib/erb_lint/linters/deprecated_classes.rb
|
80
|
+
- lib/erb_lint/linters/erb_safety.rb
|
52
81
|
- lib/erb_lint/linters/final_newline.rb
|
53
82
|
- lib/erb_lint/runner.rb
|
54
83
|
homepage: https://github.com/justinthec/erb-lint
|