synvert-core 1.23.1 → 1.25.0
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/CHANGELOG.md +13 -1
- data/Gemfile.lock +3 -3
- data/README.md +1 -1
- data/lib/synvert/core/engine/elegant.rb +125 -0
- data/lib/synvert/core/engine/erb.rb +2 -2
- data/lib/synvert/core/engine/haml.rb +40 -55
- data/lib/synvert/core/engine/slim.rb +98 -0
- data/lib/synvert/core/engine.rb +3 -0
- data/lib/synvert/core/rewriter/instance.rb +7 -5
- data/lib/synvert/core/version.rb +1 -1
- data/lib/synvert/core.rb +4 -2
- data/spec/spec_helper.rb +1 -0
- data/spec/synvert/core/engine/erb_spec.rb +4 -0
- data/spec/synvert/core/engine/haml_spec.rb +161 -3
- data/spec/synvert/core/engine/slim_spec.rb +223 -0
- data/spec/synvert/core/rewriter/instance_spec.rb +53 -3
- data/synvert-core-ruby.gemspec +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 69a5a31926536922320b7184cae70096bba437729c6c4dbe6c0bb2ab98d56e43
|
|
4
|
+
data.tar.gz: afeed86dae83348eccb85859dcc907250ff1e5361d69b7a71839cb88c1fc3e79
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50d06d1c664c06fac3e7bc163226c3e98305bd26d802919ce73003ad4e8d5e1cf441d2559b32aeada9266d48cb7d3365ef2d39fa37a934bd2e0fe2ad489693ee
|
|
7
|
+
data.tar.gz: 34915fcbb4252d66c9b25318b96e1f4bf6f4a8881f8d85229bc7f6f5cbf80ff50aed3f774cddcbab10495ef865daff0c6d40fd6608382e9e97d7a42b16ca6fc3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.25.0 (2023-04-17)
|
|
4
|
+
|
|
5
|
+
* Update `wrap` dsl
|
|
6
|
+
* Update `node_mutation` to 1.15.1
|
|
7
|
+
* Fix `erb` encoded source
|
|
8
|
+
|
|
9
|
+
## 1.24.0 (2023-04-16)
|
|
10
|
+
|
|
11
|
+
* Add `Slim` engine
|
|
12
|
+
* Abstract `Engine/Elegant` module
|
|
13
|
+
* Rewrite `Haml` and `Slim` engine by StringScanner
|
|
14
|
+
|
|
3
15
|
## 1.23.1 (2023-04-06)
|
|
4
16
|
|
|
5
17
|
* Fix array index name in `GotoScope`
|
|
@@ -7,7 +19,7 @@
|
|
|
7
19
|
|
|
8
20
|
## 1.23.0 (2023-04-05)
|
|
9
21
|
|
|
10
|
-
* Add
|
|
22
|
+
* Add `Haml` engine
|
|
11
23
|
* Add `Engine.register` and `Engine.encode` and `Engine.generate_transform_proc`
|
|
12
24
|
* Set NodeMutation `transform_proc`
|
|
13
25
|
* Update `node_mutation` to 1.14.0
|
data/Gemfile.lock
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
synvert-core (1.
|
|
4
|
+
synvert-core (1.25.0)
|
|
5
5
|
activesupport (< 7.0.0)
|
|
6
|
-
node_mutation (>= 1.
|
|
6
|
+
node_mutation (>= 1.15.1)
|
|
7
7
|
node_query (>= 1.12.1)
|
|
8
8
|
parallel
|
|
9
9
|
parser
|
|
@@ -48,7 +48,7 @@ GEM
|
|
|
48
48
|
method_source (1.0.0)
|
|
49
49
|
minitest (5.18.0)
|
|
50
50
|
nenv (0.3.0)
|
|
51
|
-
node_mutation (1.
|
|
51
|
+
node_mutation (1.15.1)
|
|
52
52
|
node_query (1.12.1)
|
|
53
53
|
notiffany (0.1.3)
|
|
54
54
|
nenv (~> 0.1)
|
data/README.md
CHANGED
|
@@ -94,7 +94,7 @@ Actions:
|
|
|
94
94
|
* [replace](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#replace-instance_method) - replace the code of specified child nodes
|
|
95
95
|
* [delete](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#delete-instance_method) - delete the code in specified child nodes
|
|
96
96
|
* [remove](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#remove-instance_method) - remove the whole code of current node
|
|
97
|
-
* [wrap](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#wrap-instance_method) - wrap the current node with code
|
|
97
|
+
* [wrap](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#wrap-instance_method) - wrap the current node with prefix and suffix code
|
|
98
98
|
* [replace_with](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#replace_with-instance_method) - replace the whole code of current node
|
|
99
99
|
* [warn](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#warn-instance_method) - warn message
|
|
100
100
|
* [replace_erb_stmt_with_expr](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#replace_erb_stmt_with_expr-instance_method) - replace erb stmt code to expr code
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synvert::Core
|
|
4
|
+
module Engine
|
|
5
|
+
# Engine::Elegant provides some helper methods for engines
|
|
6
|
+
# who read block code without end.
|
|
7
|
+
# For those engines, it will try to insert `end\n` when encode,
|
|
8
|
+
# while delete `end\n` when generate_transform_proc.
|
|
9
|
+
module Elegant
|
|
10
|
+
END_LINE = "end\n"
|
|
11
|
+
WHITESPACE = ' '
|
|
12
|
+
DO_BLOCK_REGEX = / do(\s|\z|\n)/
|
|
13
|
+
|
|
14
|
+
IF_KEYWORDS = %w[if unless begin case for while until]
|
|
15
|
+
ELSE_KEYWORDS = %w[else elsif when in rescue ensure]
|
|
16
|
+
|
|
17
|
+
# Generate transform proc, it's used to adjust start and end position of actions.
|
|
18
|
+
# Due to the fact that we insert `end\n` when encode the source code, we need to adjust
|
|
19
|
+
# start and end position of actions to match the original source code.
|
|
20
|
+
# e.g. if end\n exists in position 10, action start position is 20 and end position is 30,
|
|
21
|
+
# then action start position should be 16 and end position should be 26.
|
|
22
|
+
#
|
|
23
|
+
# @param encoded_source [String] encoded source.
|
|
24
|
+
# @return [Proc] transform proc.
|
|
25
|
+
def generate_transform_proc(encoded_source)
|
|
26
|
+
proc do |actions|
|
|
27
|
+
start = 0
|
|
28
|
+
indices = []
|
|
29
|
+
loop do
|
|
30
|
+
index = encoded_source[start..-1].index(END_LINE)
|
|
31
|
+
break unless index
|
|
32
|
+
|
|
33
|
+
indices << (start + index)
|
|
34
|
+
start += index + END_LINE.length
|
|
35
|
+
end
|
|
36
|
+
indices.each do |index|
|
|
37
|
+
actions.each do |action|
|
|
38
|
+
action.start -= END_LINE.length if action.start > index
|
|
39
|
+
action.end -= END_LINE.length if action.end > index
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Check if the current leading_spaces_count is less than or equal to the last leading_spaces_count in leading_spaces_counts.
|
|
46
|
+
# If so, pop the last leading_spaces_count and return true.
|
|
47
|
+
def insert_end?(leading_spaces_counts, current_leading_spaces_count, equal = true)
|
|
48
|
+
operation = equal ? :<= : :<
|
|
49
|
+
if !leading_spaces_counts.empty? && current_leading_spaces_count.send(operation, leading_spaces_counts.last)
|
|
50
|
+
leading_spaces_counts.pop
|
|
51
|
+
true
|
|
52
|
+
else
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def scan_ruby_statement(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
58
|
+
new_code << scanner.scan(/\s*/)
|
|
59
|
+
keyword = scanner.scan(/\w+/)
|
|
60
|
+
rest = scanner.scan(/.*?(\z|\n)/)
|
|
61
|
+
if insert_end?(leading_spaces_counts, leading_spaces_count, !ELSE_KEYWORDS.include?(keyword))
|
|
62
|
+
new_code << END_LINE
|
|
63
|
+
end
|
|
64
|
+
if IF_KEYWORDS.include?(keyword) || rest =~ DO_BLOCK_REGEX
|
|
65
|
+
leading_spaces_counts << leading_spaces_count
|
|
66
|
+
end
|
|
67
|
+
new_code << keyword
|
|
68
|
+
new_code << rest
|
|
69
|
+
|
|
70
|
+
while rest.rstrip.end_with?(',')
|
|
71
|
+
rest = scanner.scan(/.*?(\z|\n)/)
|
|
72
|
+
new_code << rest
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def scan_ruby_expression(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
77
|
+
if insert_end?(leading_spaces_counts, leading_spaces_count)
|
|
78
|
+
new_code << END_LINE
|
|
79
|
+
end
|
|
80
|
+
rest = scanner.scan(/.*?(\z|\n)/)
|
|
81
|
+
if rest =~ DO_BLOCK_REGEX
|
|
82
|
+
leading_spaces_counts << leading_spaces_count
|
|
83
|
+
end
|
|
84
|
+
new_code << rest
|
|
85
|
+
|
|
86
|
+
while rest.rstrip.end_with?(',')
|
|
87
|
+
rest = scanner.scan(/.*?(\z|\n)/)
|
|
88
|
+
new_code << rest
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def scan_ruby_interpolation_and_plain_text(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
93
|
+
if insert_end?(leading_spaces_counts, leading_spaces_count)
|
|
94
|
+
new_code << END_LINE
|
|
95
|
+
end
|
|
96
|
+
while scanner.scan(/(.*?)(\\*)#\{/) # it matches interpolation " #{current_user.login}"
|
|
97
|
+
new_code << (WHITESPACE * scanner.matched.size)
|
|
98
|
+
unless scanner.matched[-3] == '\\'
|
|
99
|
+
count = 1
|
|
100
|
+
while scanner.scan(/.*?([\{\}])/)
|
|
101
|
+
if scanner.matched[-1] == '}'
|
|
102
|
+
count -= 1
|
|
103
|
+
if count == 0
|
|
104
|
+
new_code << (scanner.matched[0..-2] + ';')
|
|
105
|
+
break
|
|
106
|
+
else
|
|
107
|
+
new_code << scanner.matched
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
count += 1
|
|
111
|
+
new_code << scanner.matched
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
if scanner.scan(/.*?\z/)
|
|
117
|
+
new_code << (WHITESPACE * scanner.matched.size)
|
|
118
|
+
end
|
|
119
|
+
if scanner.scan(/.*?\n/)
|
|
120
|
+
new_code << ((WHITESPACE * (scanner.matched.size - 1)) + "\n")
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -9,9 +9,9 @@ module Synvert::Core
|
|
|
9
9
|
# @param source [String] erb code.
|
|
10
10
|
# @return [String] encoded ruby code.
|
|
11
11
|
def encode(source)
|
|
12
|
-
source.gsub(/%>.*?<%=?/m) { |str| replace_all_code_but_white_space_characters(str) }
|
|
12
|
+
source.gsub(/%>.*?<%=?/m) { |str| ';' + replace_all_code_but_white_space_characters(str[1..-1]) }
|
|
13
13
|
.sub(/^.*?<%=?/m) { |str| replace_all_code_but_white_space_characters(str) }
|
|
14
|
-
.sub(/%>.*?$/m) { |str| replace_all_code_but_white_space_characters(str) }
|
|
14
|
+
.sub(/%>.*?$/m) { |str| ';' + replace_all_code_but_white_space_characters(str[1..-1]) }
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# Generate an empty proc.
|
|
@@ -4,7 +4,7 @@ module Synvert::Core
|
|
|
4
4
|
module Engine
|
|
5
5
|
class Haml
|
|
6
6
|
class << self
|
|
7
|
-
|
|
7
|
+
include Elegant
|
|
8
8
|
|
|
9
9
|
# Encode haml string, leave only ruby code, replace other haml code with whitespace.
|
|
10
10
|
# And insert `end\n` for each if, unless, begin, case to make it a valid ruby code.
|
|
@@ -12,70 +12,55 @@ module Synvert::Core
|
|
|
12
12
|
# @param source [String] haml code.
|
|
13
13
|
# @return [String] encoded ruby code.
|
|
14
14
|
def encode(source)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
leading_spaces_counts = []
|
|
16
|
+
new_code = []
|
|
17
|
+
scanner = StringScanner.new(source)
|
|
18
|
+
loop do
|
|
19
|
+
new_code << scanner.scan(/\s*/)
|
|
20
|
+
leading_spaces_count = scanner.matched.size
|
|
21
|
+
if scanner.scan('-') # it matches ruby statement " - current_user"
|
|
22
|
+
new_code << WHITESPACE
|
|
23
|
+
scan_ruby_statement(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
24
|
+
elsif scanner.scan('=') # it matches ruby expression " = current_user.login"
|
|
25
|
+
new_code << WHITESPACE
|
|
26
|
+
scan_ruby_expression(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
27
|
+
elsif scanner.scan(/[%#\.][a-zA-Z0-9\-_%#\.]+/) # it matches element, id and class " %span.user"
|
|
28
|
+
new_code << (WHITESPACE * scanner.matched.size)
|
|
29
|
+
scan_matching_wrapper(scanner, new_code, '{', '}')
|
|
30
|
+
if scanner.scan('=')
|
|
31
|
+
new_code << WHITESPACE
|
|
32
|
+
scan_ruby_expression(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
25
33
|
else
|
|
26
|
-
|
|
27
|
-
code = (' ' * ($1.size + ($2 || '').size + 1)) + $3
|
|
28
|
-
new_line = check_and_insert_end(code, tab_sizes, $1.size)
|
|
29
|
-
tab_sizes.push($1.size) if line.include?(' do ')
|
|
30
|
-
new_line
|
|
31
|
-
elsif line =~ /\A(\s*)(.*)/ # match any other line
|
|
32
|
-
check_and_insert_end(' ' * line.size, tab_sizes, $1.size)
|
|
33
|
-
end
|
|
34
|
+
scan_ruby_interpolation_and_plain_text(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
34
35
|
end
|
|
36
|
+
else
|
|
37
|
+
scan_ruby_interpolation_and_plain_text(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
35
38
|
end
|
|
36
|
-
lines.push(END_LINE) unless tab_sizes.empty?
|
|
37
|
-
lines.join("\n")
|
|
38
|
-
end
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# start and end position of actions to match the original source code.
|
|
43
|
-
# e.g. if end\n exists in position 10, action start position is 20 and end position is 30,
|
|
44
|
-
# then action start position should be 16 and end position should be 26.
|
|
45
|
-
#
|
|
46
|
-
# @param encoded_source [String] encoded source.
|
|
47
|
-
# @return [Proc] transform proc.
|
|
48
|
-
def generate_transform_proc(encoded_source)
|
|
49
|
-
proc do |actions|
|
|
50
|
-
start = 0
|
|
51
|
-
indices = []
|
|
52
|
-
loop do
|
|
53
|
-
index = encoded_source[start..-1].index(END_LINE)
|
|
54
|
-
break unless index
|
|
40
|
+
break if scanner.eos?
|
|
41
|
+
end
|
|
55
42
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
end
|
|
59
|
-
indices.each do |index|
|
|
60
|
-
actions.each do |action|
|
|
61
|
-
action.start -= END_LINE.length if action.start > index
|
|
62
|
-
action.end -= END_LINE.length if action.end > index
|
|
63
|
-
end
|
|
64
|
-
end
|
|
43
|
+
while leading_spaces_counts.pop
|
|
44
|
+
new_code << END_LINE
|
|
65
45
|
end
|
|
46
|
+
new_code.join
|
|
66
47
|
end
|
|
67
48
|
|
|
68
49
|
private
|
|
69
50
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
51
|
+
def scan_matching_wrapper(scanner, new_code, start, ending)
|
|
52
|
+
if scanner.scan(start) # it matches attributes " %span{:class => 'user'}"
|
|
53
|
+
new_code << start
|
|
54
|
+
count = 1
|
|
55
|
+
while scanner.scan(/.*?[#{Regexp.quote(start)}#{Regexp.quote(ending)}]/m)
|
|
56
|
+
new_code << scanner.matched
|
|
57
|
+
if scanner.matched[-1] == ending
|
|
58
|
+
count -= 1
|
|
59
|
+
break if count == 0
|
|
60
|
+
else
|
|
61
|
+
count += 1
|
|
62
|
+
end
|
|
63
|
+
end
|
|
79
64
|
end
|
|
80
65
|
end
|
|
81
66
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synvert::Core
|
|
4
|
+
module Engine
|
|
5
|
+
class Slim
|
|
6
|
+
class << self
|
|
7
|
+
include Elegant
|
|
8
|
+
|
|
9
|
+
ATTRIBUTES_PAIR = { '{' => '}', '[' => ']', '(' => ')' }
|
|
10
|
+
|
|
11
|
+
# Encode haml string, leave only ruby code, replace other haml code with whitespace.
|
|
12
|
+
# And insert `end\n` for each if, unless, begin, case to make it a valid ruby code.
|
|
13
|
+
#
|
|
14
|
+
# @param source [String] haml code.
|
|
15
|
+
# @return [String] encoded ruby code.
|
|
16
|
+
def encode(source)
|
|
17
|
+
leading_spaces_counts = []
|
|
18
|
+
new_code = []
|
|
19
|
+
scanner = StringScanner.new(source)
|
|
20
|
+
loop do
|
|
21
|
+
new_code << scanner.scan(/\s*/)
|
|
22
|
+
leading_spaces_count = scanner.matched.size
|
|
23
|
+
if scanner.scan('-') # it matches ruby statement " - current_user"
|
|
24
|
+
new_code << WHITESPACE
|
|
25
|
+
scan_ruby_statement(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
26
|
+
elsif scanner.scan(/==?/) # it matches ruby expression " = current_user.login"
|
|
27
|
+
new_code << (WHITESPACE * scanner.matched.size)
|
|
28
|
+
scan_ruby_expression(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
29
|
+
elsif scanner.scan(/[a-z#\.][a-zA-Z0-9\-_%#\.]*/) # it matches element, id and class " span.user"
|
|
30
|
+
new_code << (WHITESPACE * scanner.matched.size)
|
|
31
|
+
ATTRIBUTES_PAIR.each do |start, ending| # it matches attributes in brackets " span[ class='user' ]"
|
|
32
|
+
scan_matching_wrapper(scanner, new_code, start, ending)
|
|
33
|
+
end
|
|
34
|
+
scan_attributes_between_whitespace(scanner, new_code)
|
|
35
|
+
if scanner.scan(/ ?==?/) # it matches ruby expression " span= current_user.login"
|
|
36
|
+
new_code << (WHITESPACE * scanner.matched.size)
|
|
37
|
+
scan_ruby_expression(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
38
|
+
else
|
|
39
|
+
scan_ruby_interpolation_and_plain_text(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
scan_ruby_interpolation_and_plain_text(scanner, new_code, leading_spaces_counts, leading_spaces_count)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
break if scanner.eos?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
while leading_spaces_counts.pop
|
|
49
|
+
new_code << END_LINE
|
|
50
|
+
end
|
|
51
|
+
new_code.join
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def scan_matching_wrapper(scanner, new_code, start, ending)
|
|
57
|
+
if scanner.scan(start) # it matches attributes " %span[ class='user' ]"
|
|
58
|
+
new_code << WHITESPACE
|
|
59
|
+
count = 1
|
|
60
|
+
while scanner.scan(/.*?[#{Regexp.quote(start)}#{Regexp.quote(ending)}]/m)
|
|
61
|
+
matched = scanner.matched.gsub(/(\A| ).*?=/) { |key| WHITESPACE * key.size }
|
|
62
|
+
if scanner.matched[-1] == ending
|
|
63
|
+
new_code << (matched[0..-2] + ';')
|
|
64
|
+
count -= 1
|
|
65
|
+
break if count == 0
|
|
66
|
+
else
|
|
67
|
+
new_code << matched
|
|
68
|
+
count += 1
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def scan_attributes_between_whitespace(scanner, new_code)
|
|
75
|
+
while scanner.scan(/ ?[\w\-_]+==?/) # it matches attributes split by space " span class='user'"
|
|
76
|
+
new_code << (WHITESPACE * scanner.matched.size)
|
|
77
|
+
stack = []
|
|
78
|
+
while scanner.scan(/(.*?['"\(\)\[\]\{\}]|.+?\b)/)
|
|
79
|
+
matched = scanner.matched
|
|
80
|
+
new_code << matched
|
|
81
|
+
if ['(', '[', '{'].include?(matched[-1])
|
|
82
|
+
stack << matched[-1]
|
|
83
|
+
elsif [')', ']', '}'].include?(matched[-1])
|
|
84
|
+
stack.pop
|
|
85
|
+
elsif %w[' "].include?(matched[-1])
|
|
86
|
+
stack.last == matched[-1] ? stack.pop : stack << matched[-1]
|
|
87
|
+
end
|
|
88
|
+
break if stack.empty?
|
|
89
|
+
end
|
|
90
|
+
if scanner.scan(' ')
|
|
91
|
+
new_code << ';'
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/synvert/core/engine.rb
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
module Synvert::Core
|
|
4
4
|
# Engine defines how to encode / decode other files (like erb).
|
|
5
5
|
module Engine
|
|
6
|
+
autoload :Elegant, 'synvert/core/engine/elegant'
|
|
6
7
|
autoload :Erb, 'synvert/core/engine/erb'
|
|
7
8
|
autoload :Haml, 'synvert/core/engine/haml'
|
|
9
|
+
autoload :Slim, 'synvert/core/engine/slim'
|
|
8
10
|
|
|
9
11
|
# Register an engine
|
|
10
12
|
# @param [String] extension
|
|
@@ -36,4 +38,5 @@ module Synvert::Core
|
|
|
36
38
|
|
|
37
39
|
Engine.register('.erb', Engine::Erb)
|
|
38
40
|
Engine.register('.haml', Engine::Haml)
|
|
41
|
+
Engine.register('.slim', Engine::Slim)
|
|
39
42
|
end
|
|
@@ -362,7 +362,7 @@ module Synvert::Core
|
|
|
362
362
|
@current_mutation.delete(@current_node, *selectors, and_comma: and_comma)
|
|
363
363
|
end
|
|
364
364
|
|
|
365
|
-
# It wraps current node with code.
|
|
365
|
+
# It wraps current node with prefix and suffix code.
|
|
366
366
|
# @example
|
|
367
367
|
# # class Foobar
|
|
368
368
|
# # end
|
|
@@ -372,11 +372,13 @@ module Synvert::Core
|
|
|
372
372
|
# # end
|
|
373
373
|
# # end
|
|
374
374
|
# within_node type: 'class' do
|
|
375
|
-
# wrap with: 'module Synvert'
|
|
375
|
+
# wrap with: 'module Synvert', and: 'end', newline: true
|
|
376
376
|
# end
|
|
377
|
-
# @param
|
|
378
|
-
|
|
379
|
-
|
|
377
|
+
# @param prefix [String] prefix code need to be wrapped with.
|
|
378
|
+
# @param suffix [String] suffix code need to be wrapped with.
|
|
379
|
+
# @param newline [Boolean] if wrap code in newline, default is false
|
|
380
|
+
def wrap(prefix:, suffix:, newline: false)
|
|
381
|
+
@current_mutation.wrap(@current_node, prefix: prefix, suffix: suffix, newline: newline)
|
|
380
382
|
end
|
|
381
383
|
|
|
382
384
|
# No operation.
|
data/lib/synvert/core/version.rb
CHANGED
data/lib/synvert/core.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'synvert/core/version'
|
|
4
4
|
require 'bundler'
|
|
5
|
+
require 'strscan'
|
|
5
6
|
require 'parser'
|
|
6
7
|
require 'parser/current'
|
|
7
8
|
require 'parser_node_ext'
|
|
@@ -29,8 +30,9 @@ module Synvert
|
|
|
29
30
|
ALL_RUBY_FILES = %w[**/*.rb]
|
|
30
31
|
ALL_ERB_FILES = %w[**/*.erb]
|
|
31
32
|
ALL_HAML_FILES = %w[**/*.haml]
|
|
33
|
+
ALL_SLIM_FILES = %w[**/*.slim]
|
|
32
34
|
ALL_RAKE_FILES = %w[**/*.rake]
|
|
33
|
-
ALL_FILES = ALL_RUBY_FILES + ALL_ERB_FILES + ALL_HAML_FILES + ALL_RAKE_FILES
|
|
35
|
+
ALL_FILES = ALL_RUBY_FILES + ALL_ERB_FILES + ALL_HAML_FILES + ALL_SLIM_FILES + ALL_RAKE_FILES
|
|
34
36
|
|
|
35
37
|
RAILS_APP_FILES = %w[app/**/*.rb engines/*/app/**/*.rb]
|
|
36
38
|
RAILS_CONTROLLER_FILES = %w[app/controllers/**/*.rb engines/*/app/controllers/**/*.rb]
|
|
@@ -47,7 +49,7 @@ module Synvert
|
|
|
47
49
|
engines/*/config/routes.rb
|
|
48
50
|
engines/*/config/routes/**/*.rb
|
|
49
51
|
]
|
|
50
|
-
RAILS_VIEW_FILES = ALL_ERB_FILES + ALL_HAML_FILES
|
|
52
|
+
RAILS_VIEW_FILES = ALL_ERB_FILES + ALL_HAML_FILES + ALL_SLIM_FILES
|
|
51
53
|
|
|
52
54
|
RAILS_CONTROLLER_TEST_FILES = %w[
|
|
53
55
|
test/functional/**/*.rb
|
data/spec/spec_helper.rb
CHANGED
|
@@ -20,6 +20,8 @@ module Synvert::Core
|
|
|
20
20
|
bar = 'foo'
|
|
21
21
|
%>
|
|
22
22
|
|
|
23
|
+
<%= user.first_name %> <%= user.last_name %>
|
|
24
|
+
|
|
23
25
|
<% if User.current &&
|
|
24
26
|
User.current.admin %>
|
|
25
27
|
<%= rounded_content("page") do %>
|
|
@@ -40,6 +42,8 @@ module Synvert::Core
|
|
|
40
42
|
expect(encoded_source).to be_include 'post = Post.find(:first)'
|
|
41
43
|
expect(encoded_source).to be_include "link_to_function 'test', \"confirm('test');\""
|
|
42
44
|
expect(encoded_source).to be_include 'end'
|
|
45
|
+
expect(encoded_source).to be_include 'user.first_name ;'
|
|
46
|
+
expect(encoded_source).to be_include 'user.last_name ;'
|
|
43
47
|
expect(encoded_source).not_to be_include 'style'
|
|
44
48
|
expect(encoded_source).not_to be_include 'div'
|
|
45
49
|
end
|
|
@@ -17,11 +17,11 @@ module Synvert::Core
|
|
|
17
17
|
= "Hello!"
|
|
18
18
|
- if current_admin?
|
|
19
19
|
%strong= "Admin"
|
|
20
|
+
#error= error_message
|
|
21
|
+
.form-actions
|
|
22
|
+
= form_for @user do |f|
|
|
20
23
|
- elsif current_user?
|
|
21
24
|
%span= "User"
|
|
22
|
-
#error= error_message
|
|
23
|
-
.form-actions
|
|
24
|
-
= form_for @user do |f|
|
|
25
25
|
Test
|
|
26
26
|
EOS
|
|
27
27
|
encoded_source = Engine::Haml.encode(source)
|
|
@@ -43,6 +43,164 @@ module Synvert::Core
|
|
|
43
43
|
expect(encoded_source).not_to be_include 'Test'
|
|
44
44
|
expect(encoded_source).not_to be_include '.form-actions'
|
|
45
45
|
end
|
|
46
|
+
|
|
47
|
+
it 'encodes plain text' do
|
|
48
|
+
source = <<~EOS
|
|
49
|
+
%gee
|
|
50
|
+
%whiz
|
|
51
|
+
Wow this is cool!
|
|
52
|
+
EOS
|
|
53
|
+
encoded_source = Engine::Haml.encode(source)
|
|
54
|
+
expect(encoded_source).not_to be_include '%gee'
|
|
55
|
+
expect(encoded_source).not_to be_include '%whiz'
|
|
56
|
+
expect(encoded_source).not_to be_include 'Wow this is cool!'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'encodes escaping' do
|
|
60
|
+
source = <<~EOS
|
|
61
|
+
%title
|
|
62
|
+
= @title
|
|
63
|
+
\\= @title
|
|
64
|
+
EOS
|
|
65
|
+
encoded_source = Engine::Haml.encode(source)
|
|
66
|
+
expect(encoded_source.scan('@title').length).to eq 1
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'encodes attributes' do
|
|
70
|
+
source = <<~EOS
|
|
71
|
+
%html{:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en"}
|
|
72
|
+
EOS
|
|
73
|
+
encoded_source = Engine::Haml.encode(source)
|
|
74
|
+
expect(encoded_source).to be_include '{:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en"}'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'encodes multiple lines attributes' do
|
|
78
|
+
source = <<~EOS
|
|
79
|
+
%script{
|
|
80
|
+
"type": "text/javascript",
|
|
81
|
+
"src": "javascripts/script_9",
|
|
82
|
+
"data": {
|
|
83
|
+
"controller": "reporter",
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
EOS
|
|
87
|
+
encoded_source = Engine::Haml.encode(source)
|
|
88
|
+
expect(encoded_source).to be_include '"type": "text/javascript",'
|
|
89
|
+
expect(encoded_source).to be_include '"src": "javascripts/script_9",'
|
|
90
|
+
expect(encoded_source).to be_include '"controller": "reporter",'
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'encodes prefixed attributes' do
|
|
94
|
+
source = <<~EOS
|
|
95
|
+
%a{:href=>"/posts", :data => {:author_id => 123, :category => 7}} Posts By Author
|
|
96
|
+
EOS
|
|
97
|
+
encoded_source = Engine::Haml.encode(source)
|
|
98
|
+
expect(encoded_source).to be_include '{:href=>"/posts", :data => {:author_id => 123, :category => 7}}'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'encodes class and ID' do
|
|
102
|
+
source = <<~EOS
|
|
103
|
+
%div#things
|
|
104
|
+
%span#rice Chicken Fried
|
|
105
|
+
%p.beans{ :food => 'true' } The magical fruit
|
|
106
|
+
%h1.class.otherclass#id La La La
|
|
107
|
+
EOS
|
|
108
|
+
encoded_source = Engine::Haml.encode(source)
|
|
109
|
+
expect(encoded_source).not_to be_include '%div'
|
|
110
|
+
expect(encoded_source).not_to be_include '#things'
|
|
111
|
+
expect(encoded_source).not_to be_include '.beans'
|
|
112
|
+
expect(encoded_source).not_to be_include '.class'
|
|
113
|
+
expect(encoded_source).not_to be_include '#id'
|
|
114
|
+
expect(encoded_source).to be_include "{ :food => 'true' }"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'encodes haml comments' do
|
|
118
|
+
source = <<~EOS
|
|
119
|
+
%p foo
|
|
120
|
+
-# This is a comment
|
|
121
|
+
%p bar
|
|
122
|
+
EOS
|
|
123
|
+
encoded_source = Engine::Haml.encode(source)
|
|
124
|
+
expect(encoded_source).not_to be_include 'foo'
|
|
125
|
+
expect(encoded_source).not_to be_include 'bar'
|
|
126
|
+
expect(encoded_source).to be_include '# This is a comment'
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'encodes ruby evaluation' do
|
|
130
|
+
source = <<~EOS
|
|
131
|
+
%p
|
|
132
|
+
= ['hi', 'there', 'reader!'].join " "
|
|
133
|
+
= "yo"
|
|
134
|
+
EOS
|
|
135
|
+
encoded_source = Engine::Haml.encode(source)
|
|
136
|
+
expect(encoded_source).to be_include "['hi', 'there', 'reader!'].join \" \""
|
|
137
|
+
expect(encoded_source).to be_include '"yo"'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'encodes ruby evaluation in the same line' do
|
|
141
|
+
source = <<~EOS
|
|
142
|
+
%p= "hello"
|
|
143
|
+
EOS
|
|
144
|
+
encoded_source = Engine::Haml.encode(source)
|
|
145
|
+
expect(encoded_source).not_to be_include '%p'
|
|
146
|
+
expect(encoded_source).to be_include '"hello"'
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'encodes multiple lines ruby evaluation' do
|
|
150
|
+
source = <<~EOS
|
|
151
|
+
= link_to_remote "Add to cart",
|
|
152
|
+
:url => { :action => "add", :id => product.id },
|
|
153
|
+
:update => { :success => "cart", :failure => "error" }
|
|
154
|
+
EOS
|
|
155
|
+
encoded_source = Engine::Haml.encode(source)
|
|
156
|
+
expect(encoded_source).to be_include 'link_to_remote "Add to cart",'
|
|
157
|
+
expect(encoded_source).to be_include ':url => { :action => "add", :id => product.id },'
|
|
158
|
+
expect(encoded_source).to be_include ':update => { :success => "cart", :failure => "error" }'
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'encodes running ruby' do
|
|
162
|
+
source = <<~EOS
|
|
163
|
+
- foo = "hello"
|
|
164
|
+
- foo << " there"
|
|
165
|
+
- foo << " you!"
|
|
166
|
+
EOS
|
|
167
|
+
encoded_source = Engine::Haml.encode(source)
|
|
168
|
+
expect(encoded_source).to be_include 'foo = "hello"'
|
|
169
|
+
expect(encoded_source).to be_include 'foo << " there"'
|
|
170
|
+
expect(encoded_source).to be_include 'foo << " you!"'
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'encodes multiple line running ruby' do
|
|
174
|
+
source = <<~EOS
|
|
175
|
+
- links = {:home => "/",
|
|
176
|
+
:docs => "/docs",
|
|
177
|
+
:about => "/about"}
|
|
178
|
+
EOS
|
|
179
|
+
encoded_source = Engine::Haml.encode(source)
|
|
180
|
+
expect(encoded_source).to be_include 'links = {:home => "/",'
|
|
181
|
+
expect(encoded_source).to be_include ':docs => "/docs",'
|
|
182
|
+
expect(encoded_source).to be_include ':about => "/about"}'
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it 'encodes ruby block' do
|
|
186
|
+
source = <<~EOS
|
|
187
|
+
- (42...47).each do |i|
|
|
188
|
+
%p= i
|
|
189
|
+
%p See, I can count!
|
|
190
|
+
EOS
|
|
191
|
+
encoded_source = Engine::Haml.encode(source)
|
|
192
|
+
expect(encoded_source).to be_include '(42...47).each do |i|'
|
|
193
|
+
expect(encoded_source).to be_include 'end'
|
|
194
|
+
expect(encoded_source).not_to be_include 'See, I can count!'
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it 'encodes ruby interpolation' do
|
|
198
|
+
source = 'Look at #{h word} lack of backslash: \#{foo}'
|
|
199
|
+
encoded_source = Engine::Haml.encode(source)
|
|
200
|
+
expect(encoded_source).to be_include 'h word;'
|
|
201
|
+
expect(encoded_source).not_to be_include 'foo'
|
|
202
|
+
expect(encoded_source).not_to be_include 'lack of backslash:'
|
|
203
|
+
end
|
|
46
204
|
end
|
|
47
205
|
|
|
48
206
|
describe '#generate_transform_proc' do
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
module Synvert::Core
|
|
6
|
+
describe Engine::Slim do
|
|
7
|
+
describe '#encode' do
|
|
8
|
+
it 'encodes source' do
|
|
9
|
+
source = <<~EOS
|
|
10
|
+
doctype html
|
|
11
|
+
html
|
|
12
|
+
head
|
|
13
|
+
title Slim Examples
|
|
14
|
+
meta name="keywords" content="template language"
|
|
15
|
+
meta name="author" content=author
|
|
16
|
+
javascript:
|
|
17
|
+
alert('Slim supports embedded javascript!')
|
|
18
|
+
|
|
19
|
+
body
|
|
20
|
+
h1 Markup examples
|
|
21
|
+
|
|
22
|
+
#content
|
|
23
|
+
p This example shows you what a basic Slim file looks like.
|
|
24
|
+
|
|
25
|
+
== yield
|
|
26
|
+
|
|
27
|
+
- unless items.empty?
|
|
28
|
+
table
|
|
29
|
+
- items.each do |item|
|
|
30
|
+
tr
|
|
31
|
+
td.name = item.name
|
|
32
|
+
td.price = item.price
|
|
33
|
+
- else
|
|
34
|
+
p
|
|
35
|
+
| No items found. Please add some inventory.
|
|
36
|
+
Thank you!
|
|
37
|
+
|
|
38
|
+
div id="footer"
|
|
39
|
+
= render 'footer'
|
|
40
|
+
| Copyright © \#{year} \#{author}
|
|
41
|
+
EOS
|
|
42
|
+
encoded_source = Engine::Slim.encode(source)
|
|
43
|
+
expect(encoded_source).to be_include 'yield'
|
|
44
|
+
expect(encoded_source).to be_include 'unless items.empty?'
|
|
45
|
+
expect(encoded_source).to be_include 'items.each do |item|'
|
|
46
|
+
expect(encoded_source).to be_include 'item.name'
|
|
47
|
+
expect(encoded_source).to be_include 'item.price'
|
|
48
|
+
expect(encoded_source).to be_include "render 'footer'"
|
|
49
|
+
expect(encoded_source).to be_include 'year'
|
|
50
|
+
expect(encoded_source).to be_include 'author'
|
|
51
|
+
expect(encoded_source.scan("end\n").length).to eq 2
|
|
52
|
+
expect(encoded_source).not_to be_include 'html'
|
|
53
|
+
expect(encoded_source).not_to be_include 'meta'
|
|
54
|
+
expect(encoded_source).not_to be_include 'javascript:'
|
|
55
|
+
expect(encoded_source).not_to be_include 'Markup examples'
|
|
56
|
+
expect(encoded_source).not_to be_include 'td.name'
|
|
57
|
+
expect(encoded_source).not_to be_include 'td.price'
|
|
58
|
+
expect(encoded_source).not_to be_include 'Copyright'
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'encodes control code' do
|
|
62
|
+
source = <<~EOS
|
|
63
|
+
body
|
|
64
|
+
- if articles.empty?
|
|
65
|
+
| No inventory
|
|
66
|
+
EOS
|
|
67
|
+
encoded_source = Engine::Slim.encode(source)
|
|
68
|
+
expect(encoded_source).to be_include 'if articles.empty?'
|
|
69
|
+
expect(encoded_source).to be_include 'end'
|
|
70
|
+
expect(encoded_source).not_to be_include 'No inventory'
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'encodes output' do
|
|
74
|
+
source = <<~EOS
|
|
75
|
+
= javascript_include_tag \
|
|
76
|
+
"jquery",
|
|
77
|
+
"application"
|
|
78
|
+
EOS
|
|
79
|
+
encoded_source = Engine::Slim.encode(source)
|
|
80
|
+
expect(encoded_source).to be_include 'javascript_include_tag'
|
|
81
|
+
expect(encoded_source).to be_include '"jquery",'
|
|
82
|
+
expect(encoded_source).to be_include '"application"'
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'encodes output without html escaping' do
|
|
86
|
+
source = <<~EOS
|
|
87
|
+
== yield
|
|
88
|
+
EOS
|
|
89
|
+
encoded_source = Engine::Slim.encode(source)
|
|
90
|
+
expect(encoded_source).to be_include 'yield'
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'encodes tags' do
|
|
94
|
+
source = <<~EOS
|
|
95
|
+
ul
|
|
96
|
+
li.first: a href="/a" A link
|
|
97
|
+
li: a href="/b" B link
|
|
98
|
+
EOS
|
|
99
|
+
encoded_source = Engine::Slim.encode(source)
|
|
100
|
+
expect(encoded_source).not_to be_include 'ul'
|
|
101
|
+
expect(encoded_source).not_to be_include 'li'
|
|
102
|
+
expect(encoded_source).not_to be_include 'first'
|
|
103
|
+
expect(encoded_source).not_to be_include 'link'
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'encodes text content' do
|
|
107
|
+
source = <<~EOS
|
|
108
|
+
body
|
|
109
|
+
h1 id="headline" Welcome to my site.
|
|
110
|
+
EOS
|
|
111
|
+
encoded_source = Engine::Slim.encode(source)
|
|
112
|
+
expect(encoded_source).not_to be_include 'h1'
|
|
113
|
+
expect(encoded_source).not_to be_include 'id'
|
|
114
|
+
expect(encoded_source).not_to be_include 'Welcome'
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'encodes dynamic content' do
|
|
118
|
+
source = <<~EOS
|
|
119
|
+
body
|
|
120
|
+
h1 id="headline" = page_headline
|
|
121
|
+
EOS
|
|
122
|
+
encoded_source = Engine::Slim.encode(source)
|
|
123
|
+
expect(encoded_source).to be_include 'page_headline'
|
|
124
|
+
expect(encoded_source).not_to be_include 'h1'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'encodes attributes wrapper' do
|
|
128
|
+
source = <<~EOS
|
|
129
|
+
body
|
|
130
|
+
h1(id="logo") = page_logo
|
|
131
|
+
h2[id="tagline" class="small tagline"] = page_tagline
|
|
132
|
+
EOS
|
|
133
|
+
encoded_source = Engine::Slim.encode(source)
|
|
134
|
+
expect(encoded_source).to be_include 'page_logo'
|
|
135
|
+
expect(encoded_source).to be_include 'page_tagline'
|
|
136
|
+
expect(encoded_source).not_to be_include 'h1'
|
|
137
|
+
expect(encoded_source).not_to be_include 'h2'
|
|
138
|
+
expect(encoded_source).not_to be_include 'id'
|
|
139
|
+
expect(encoded_source).not_to be_include 'class'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'encodes multiple lines attributes wrapper' do
|
|
143
|
+
source = <<~EOS
|
|
144
|
+
h2[id="tagline"
|
|
145
|
+
class="small tagline"] = page_tagline
|
|
146
|
+
EOS
|
|
147
|
+
encoded_source = Engine::Slim.encode(source)
|
|
148
|
+
expect(encoded_source).to be_include 'page_tagline'
|
|
149
|
+
expect(encoded_source).not_to be_include 'h2'
|
|
150
|
+
expect(encoded_source).not_to be_include 'id'
|
|
151
|
+
expect(encoded_source).not_to be_include 'class'
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'encodes ruby attributes' do
|
|
155
|
+
source = <<~EOS
|
|
156
|
+
body
|
|
157
|
+
table
|
|
158
|
+
- for user in users
|
|
159
|
+
td id="user_\#{user.id}" class=user.role
|
|
160
|
+
a href=user_action(user, :edit) Edit \#{user.first_name}
|
|
161
|
+
a href=(path_to_user user) = user.last_name
|
|
162
|
+
EOS
|
|
163
|
+
encoded_source = Engine::Slim.encode(source)
|
|
164
|
+
expect(encoded_source).to be_include 'for user in users'
|
|
165
|
+
expect(encoded_source).to be_include 'user.id'
|
|
166
|
+
expect(encoded_source).to be_include 'user_action(user, :edit)'
|
|
167
|
+
expect(encoded_source).to be_include 'user.first_name'
|
|
168
|
+
expect(encoded_source).to be_include '(path_to_user user)'
|
|
169
|
+
expect(encoded_source).to be_include 'user.last_name'
|
|
170
|
+
expect(encoded_source).to be_include 'end'
|
|
171
|
+
expect(encoded_source).not_to be_include 'href'
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it 'encodes ruby attributes with ==' do
|
|
175
|
+
source = 'a href==action_path(:start)'
|
|
176
|
+
encoded_source = Engine::Slim.encode(source)
|
|
177
|
+
expect(encoded_source).to be_include 'action_path(:start)'
|
|
178
|
+
expect(encoded_source).not_to be_include 'href'
|
|
179
|
+
expect(encoded_source).not_to be_include '='
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it 'encodes text interpolation' do
|
|
183
|
+
source = <<~EOS
|
|
184
|
+
body
|
|
185
|
+
h1 Welcome \#{current_user.name} to the show.
|
|
186
|
+
EOS
|
|
187
|
+
encoded_source = Engine::Slim.encode(source)
|
|
188
|
+
expect(encoded_source).to be_include 'current_user.name;'
|
|
189
|
+
expect(encoded_source).not_to be_include 'Welcome'
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
describe '#generate_transform_proc' do
|
|
194
|
+
it 'generates transform proc' do
|
|
195
|
+
encoded_source = <<~EOS
|
|
196
|
+
now = DateTime.now
|
|
197
|
+
if now > DateTime.parse("December 31, 2006")
|
|
198
|
+
else
|
|
199
|
+
end
|
|
200
|
+
if current_admin?
|
|
201
|
+
elsif current_user?
|
|
202
|
+
end
|
|
203
|
+
DateTime.now - now
|
|
204
|
+
EOS
|
|
205
|
+
proc = Engine::Slim.generate_transform_proc(encoded_source)
|
|
206
|
+
actions = [
|
|
207
|
+
NodeMutation::Struct::Action.new(50, 55, ''),
|
|
208
|
+
# first end position is 69
|
|
209
|
+
NodeMutation::Struct::Action.new(100, 105, ''),
|
|
210
|
+
# second end position is 111
|
|
211
|
+
NodeMutation::Struct::Action.new(120, 125, '')
|
|
212
|
+
]
|
|
213
|
+
proc.call(actions)
|
|
214
|
+
expect(actions.first.start).to eq 50
|
|
215
|
+
expect(actions.first.end).to eq 55
|
|
216
|
+
expect(actions.second.start).to eq 96
|
|
217
|
+
expect(actions.second.end).to eq 101
|
|
218
|
+
expect(actions.third.start).to eq 112
|
|
219
|
+
expect(actions.third.end).to eq 117
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
@@ -233,14 +233,16 @@ module Synvert::Core
|
|
|
233
233
|
instance.delete :dot, :message, and_comma: true
|
|
234
234
|
end
|
|
235
235
|
|
|
236
|
-
it 'parses wrap
|
|
236
|
+
it 'parses wrap' do
|
|
237
237
|
instance.instance_variable_set(:@current_mutation, double)
|
|
238
238
|
instance.current_node = double
|
|
239
239
|
expect(instance.instance_variable_get(:@current_mutation)).to receive(:wrap).with(
|
|
240
240
|
instance.current_node,
|
|
241
|
-
|
|
241
|
+
prefix: 'module Foobar',
|
|
242
|
+
suffix: 'end',
|
|
243
|
+
newline: true
|
|
242
244
|
)
|
|
243
|
-
instance.wrap
|
|
245
|
+
instance.wrap prefix: 'module Foobar', suffix: 'end', newline: true
|
|
244
246
|
end
|
|
245
247
|
|
|
246
248
|
it 'parses noop' do
|
|
@@ -385,6 +387,26 @@ module Synvert::Core
|
|
|
385
387
|
expect(File).to receive(:write).with('./app/views/posts/_form.html.haml', output)
|
|
386
388
|
instance.process
|
|
387
389
|
end
|
|
390
|
+
|
|
391
|
+
it 'updates slim file' do
|
|
392
|
+
instance =
|
|
393
|
+
Rewriter::Instance.new rewriter, 'app/views/posts/_form.html.slim' do
|
|
394
|
+
with_node node_type: 'ivar' do
|
|
395
|
+
replace_with 'post'
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
input = <<~EOS
|
|
399
|
+
= form_for @post do |f|
|
|
400
|
+
= form_for @post do |f|
|
|
401
|
+
EOS
|
|
402
|
+
output = <<~EOS
|
|
403
|
+
= form_for post do |f|
|
|
404
|
+
= form_for post do |f|
|
|
405
|
+
EOS
|
|
406
|
+
allow(File).to receive(:read).with('./app/views/posts/_form.html.slim', encoding: 'UTF-8').and_return(input)
|
|
407
|
+
expect(File).to receive(:write).with('./app/views/posts/_form.html.slim', output)
|
|
408
|
+
instance.process
|
|
409
|
+
end
|
|
388
410
|
end
|
|
389
411
|
|
|
390
412
|
describe '#test' do
|
|
@@ -477,6 +499,34 @@ module Synvert::Core
|
|
|
477
499
|
),
|
|
478
500
|
]
|
|
479
501
|
end
|
|
502
|
+
|
|
503
|
+
it 'updates slim file' do
|
|
504
|
+
instance =
|
|
505
|
+
Rewriter::Instance.new rewriter, 'app/views/posts/_form.html.slim' do
|
|
506
|
+
with_node node_type: 'ivar' do
|
|
507
|
+
replace_with 'post'
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
input = <<~EOS
|
|
511
|
+
= form_for @post do |f|
|
|
512
|
+
= form_for @post do |f|
|
|
513
|
+
EOS
|
|
514
|
+
output = <<~EOS
|
|
515
|
+
= form_for post do |f|
|
|
516
|
+
= form_for post do |f|
|
|
517
|
+
EOS
|
|
518
|
+
allow(File).to receive(:read).with('./app/views/posts/_form.html.slim', encoding: 'UTF-8').and_return(input)
|
|
519
|
+
result = instance.test
|
|
520
|
+
expect(result.file_path).to eq 'app/views/posts/_form.html.slim'
|
|
521
|
+
expect(result.actions).to eq [
|
|
522
|
+
NodeMutation::Struct::Action.new("= form_for ".length, "= form_for @post".length, 'post'),
|
|
523
|
+
NodeMutation::Struct::Action.new(
|
|
524
|
+
"= form_for @post do |f|\n= form_for ".length,
|
|
525
|
+
"= form_for @post do |f|\n= form_for @post".length,
|
|
526
|
+
'post'
|
|
527
|
+
),
|
|
528
|
+
]
|
|
529
|
+
end
|
|
480
530
|
end
|
|
481
531
|
|
|
482
532
|
describe '#process_with_node' do
|
data/synvert-core-ruby.gemspec
CHANGED
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
|
|
22
22
|
spec.add_runtime_dependency "activesupport", "< 7.0.0"
|
|
23
23
|
spec.add_runtime_dependency "node_query", ">= 1.12.1"
|
|
24
|
-
spec.add_runtime_dependency "node_mutation", ">= 1.
|
|
24
|
+
spec.add_runtime_dependency "node_mutation", ">= 1.15.1"
|
|
25
25
|
spec.add_runtime_dependency "parser"
|
|
26
26
|
spec.add_runtime_dependency "parser_node_ext", ">= 1.0.0"
|
|
27
27
|
spec.add_runtime_dependency "parallel"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: synvert-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.25.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Richard Huang
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-04-
|
|
11
|
+
date: 2023-04-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -44,14 +44,14 @@ dependencies:
|
|
|
44
44
|
requirements:
|
|
45
45
|
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: 1.
|
|
47
|
+
version: 1.15.1
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 1.
|
|
54
|
+
version: 1.15.1
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: parser
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -115,8 +115,10 @@ files:
|
|
|
115
115
|
- lib/synvert/core.rb
|
|
116
116
|
- lib/synvert/core/configuration.rb
|
|
117
117
|
- lib/synvert/core/engine.rb
|
|
118
|
+
- lib/synvert/core/engine/elegant.rb
|
|
118
119
|
- lib/synvert/core/engine/erb.rb
|
|
119
120
|
- lib/synvert/core/engine/haml.rb
|
|
121
|
+
- lib/synvert/core/engine/slim.rb
|
|
120
122
|
- lib/synvert/core/node_ext.rb
|
|
121
123
|
- lib/synvert/core/rewriter.rb
|
|
122
124
|
- lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb
|
|
@@ -140,6 +142,7 @@ files:
|
|
|
140
142
|
- spec/support/parser_helper.rb
|
|
141
143
|
- spec/synvert/core/engine/erb_spec.rb
|
|
142
144
|
- spec/synvert/core/engine/haml_spec.rb
|
|
145
|
+
- spec/synvert/core/engine/slim_spec.rb
|
|
143
146
|
- spec/synvert/core/node_ext_spec.rb
|
|
144
147
|
- spec/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action_spec.rb
|
|
145
148
|
- spec/synvert/core/rewriter/condition/if_exist_condition_spec.rb
|
|
@@ -185,6 +188,7 @@ test_files:
|
|
|
185
188
|
- spec/support/parser_helper.rb
|
|
186
189
|
- spec/synvert/core/engine/erb_spec.rb
|
|
187
190
|
- spec/synvert/core/engine/haml_spec.rb
|
|
191
|
+
- spec/synvert/core/engine/slim_spec.rb
|
|
188
192
|
- spec/synvert/core/node_ext_spec.rb
|
|
189
193
|
- spec/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action_spec.rb
|
|
190
194
|
- spec/synvert/core/rewriter/condition/if_exist_condition_spec.rb
|