synvert-core 1.21.3 → 1.21.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/CHANGELOG.md +10 -0
- data/Gemfile.lock +1 -1
- data/lib/synvert/core/engine/erb.rb +3 -132
- data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +3 -16
- data/lib/synvert/core/rewriter/instance.rb +4 -22
- data/lib/synvert/core/version.rb +1 -1
- data/spec/synvert/core/engine/erb_spec.rb +7 -10
- data/spec/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action_spec.rb +4 -4
- data/spec/synvert/core/rewriter/instance_spec.rb +39 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb89b1bc2d50d73f656541dc90fb5efad3c5637589a71c1fcba29324244c6539
|
4
|
+
data.tar.gz: d14d4b677a1b3f00b913c2baf8a0f6ef1cd8c3630f2c66a6723b891d3a0f2d83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 452e3d690d566304b0d2f7dc37aaba39da839eb6151b0720668dcb0b4e49901742172e523de40cca4e83fbb9064e04ad2d7928e6b23eca68bce0984566d10345
|
7
|
+
data.tar.gz: 7ac8916b752c005dae8c0bf958161c5b217111cacaf58968c5dd46aa2f7d4641591590fd7185dadffe0e53e63b4d7ec2a55e3ab94ec000617c618e2109f33767
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 1.21.5 (2023-02-15)
|
4
|
+
|
5
|
+
* Rewrite erb engine, no need to decode
|
6
|
+
* Rewrite `ReplaceErbStmtWithExprAction` to insert "=" instead
|
7
|
+
* Erb encoded source passed to `node_query`, but not `node_mutation`
|
8
|
+
|
9
|
+
## 1.21.4 (2023-02-13)
|
10
|
+
|
11
|
+
* Do not rescue `NoMethodError` and output debug info
|
12
|
+
|
3
13
|
## 1.21.3 (2023-02-12)
|
4
14
|
|
5
15
|
* Update `parser_node_ext` to 1.0.0
|
data/Gemfile.lock
CHANGED
@@ -4,9 +4,6 @@ require 'erubis'
|
|
4
4
|
|
5
5
|
module Synvert::Core
|
6
6
|
module Engine
|
7
|
-
ERUBY_EXPR_SPLITTER = '; ;'
|
8
|
-
ERUBY_STMT_SPLITTER = '; ;'
|
9
|
-
|
10
7
|
class Erb
|
11
8
|
class << self
|
12
9
|
# convert erb to ruby code.
|
@@ -14,135 +11,9 @@ module Synvert::Core
|
|
14
11
|
# @param source [String] erb source code.
|
15
12
|
# @return [String] ruby source code.
|
16
13
|
def encode(source)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# convert ruby code to erb.
|
21
|
-
#
|
22
|
-
# @param source [String] ruby source code.
|
23
|
-
# @return [String] erb source code.
|
24
|
-
def decode(source)
|
25
|
-
source = decode_ruby_stmt(source)
|
26
|
-
source = decode_ruby_output(source)
|
27
|
-
source = decode_html_output(source)
|
28
|
-
source = remove_erubis_buf(source)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def decode_ruby_stmt(source)
|
34
|
-
source.gsub(/#{ERUBY_STMT_SPLITTER}(.+?)#{ERUBY_STMT_SPLITTER}/mo) { "<%#{Regexp.last_match(1)}%>" }
|
35
|
-
end
|
36
|
-
|
37
|
-
def decode_ruby_output(source)
|
38
|
-
source.gsub(/@output_buffer.append=\((.+?)\);#{ERUBY_EXPR_SPLITTER}/mo) {
|
39
|
-
"<%=#{Regexp.last_match(1)}%>"
|
40
|
-
}.gsub(/@output_buffer.append= (.+?)\s+(do|\{)(\s*\|[^|]*\|)?\s*#{ERUBY_EXPR_SPLITTER}/mo) { |m|
|
41
|
-
"<%=#{m.sub('@output_buffer.append= ', '').sub(ERUBY_EXPR_SPLITTER, '')}%>"
|
42
|
-
}
|
43
|
-
end
|
44
|
-
|
45
|
-
def decode_html_output(source)
|
46
|
-
source.gsub(/@output_buffer.safe_append='(.+?)'.freeze;/m) { reverse_escape_text(Regexp.last_match(1)) }
|
47
|
-
.gsub(
|
48
|
-
/@output_buffer.safe_append=\((.+?)\);#{ERUBY_EXPR_SPLITTER}/mo
|
49
|
-
) { reverse_escape_text(Regexp.last_match(1)) }
|
50
|
-
.gsub(
|
51
|
-
/@output_buffer.safe_append=(.+?)\s+(do|\{)(\s*\|[^|]*\|)?\s*#{ERUBY_EXPR_SPLITTER}/mo
|
52
|
-
) { reverse_escape_text(Regexp.last_match(1)) }
|
53
|
-
end
|
54
|
-
|
55
|
-
def remove_erubis_buf(source)
|
56
|
-
source
|
57
|
-
.sub('@output_buffer = output_buffer || ActionView::OutputBuffer.new;', '')
|
58
|
-
.sub('@output_buffer.to_s', '')
|
59
|
-
end
|
60
|
-
|
61
|
-
def reverse_escape_text(source)
|
62
|
-
source.gsub("\\\\", "\\").gsub("\\'", "'")
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
# borrowed from rails
|
68
|
-
class Erubis < ::Erubis::Eruby
|
69
|
-
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
70
|
-
def add_preamble(src)
|
71
|
-
@newline_pending = 0
|
72
|
-
src << '@output_buffer = output_buffer || ActionView::OutputBuffer.new;'
|
73
|
-
end
|
74
|
-
|
75
|
-
def add_text(src, text)
|
76
|
-
return if text.empty?
|
77
|
-
|
78
|
-
if text == "\n"
|
79
|
-
@newline_pending += 1
|
80
|
-
else
|
81
|
-
src << "@output_buffer.safe_append='"
|
82
|
-
src << ("\n" * @newline_pending) if @newline_pending > 0
|
83
|
-
src << escape_text(text)
|
84
|
-
src << "'.freeze;"
|
85
|
-
|
86
|
-
@newline_pending = 0
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# Erubis toggles <%= and <%== behavior when escaping is enabled.
|
91
|
-
# We override to always treat <%== as escaped.
|
92
|
-
def add_expr(src, code, indicator)
|
93
|
-
case indicator
|
94
|
-
when '=='
|
95
|
-
add_expr_escaped(src, code)
|
96
|
-
else
|
97
|
-
super
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def add_expr_literal(src, code)
|
102
|
-
flush_newline_if_pending(src)
|
103
|
-
if BLOCK_EXPR.match?(code)
|
104
|
-
src << '@output_buffer.append= ' << code << ERUBY_EXPR_SPLITTER
|
105
|
-
else
|
106
|
-
src << '@output_buffer.append=(' << code << ');' << ERUBY_EXPR_SPLITTER
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def add_expr_escaped(src, code)
|
111
|
-
flush_newline_if_pending(src)
|
112
|
-
if BLOCK_EXPR.match?(code)
|
113
|
-
src << '@output_buffer.safe_append= ' << code << ERUBY_EXPR_SPLITTER
|
114
|
-
else
|
115
|
-
src << '@output_buffer.safe_append=(' << code << ');' << ERUBY_EXPR_SPLITTER
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def add_stmt(src, code)
|
120
|
-
flush_newline_if_pending(src)
|
121
|
-
if code != "\n" && code != ''
|
122
|
-
index =
|
123
|
-
case code
|
124
|
-
when /\A(\s*)\r?\n/
|
125
|
-
Regexp.last_match(1).length
|
126
|
-
when /\A(\s+)/
|
127
|
-
Regexp.last_match(1).end_with?(' ') ? Regexp.last_match(1).length - 1 : Regexp.last_match(1).length
|
128
|
-
else
|
129
|
-
0
|
130
|
-
end
|
131
|
-
code.insert(index, ERUBY_STMT_SPLITTER)
|
132
|
-
code.insert(-1, ERUBY_STMT_SPLITTER[0...-1])
|
133
|
-
end
|
134
|
-
super
|
135
|
-
end
|
136
|
-
|
137
|
-
def add_postamble(src)
|
138
|
-
flush_newline_if_pending(src)
|
139
|
-
src << '@output_buffer.to_s'
|
140
|
-
end
|
141
|
-
|
142
|
-
def flush_newline_if_pending(src)
|
143
|
-
if @newline_pending > 0
|
144
|
-
src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
|
145
|
-
@newline_pending = 0
|
14
|
+
source.gsub(/^.*?<%=?/m) { |str| ' ' * str.size }
|
15
|
+
.gsub(/%>.*?$/m) { |str| ' ' * str.size }
|
16
|
+
.gsub(/%>.*?<%=?/m) { |str| ' ' * str.size }
|
146
17
|
end
|
147
18
|
end
|
148
19
|
end
|
@@ -16,28 +16,15 @@ module Synvert::Core
|
|
16
16
|
#
|
17
17
|
# @return [String] new code.
|
18
18
|
def new_code
|
19
|
-
|
20
|
-
.sub(Engine::ERUBY_STMT_SPLITTER, '@output_buffer.append= ')
|
21
|
-
.sub(Engine::ERUBY_STMT_SPLITTER, Engine::ERUBY_EXPR_SPLITTER)
|
19
|
+
'='
|
22
20
|
end
|
23
21
|
|
24
22
|
private
|
25
23
|
|
26
24
|
# Calculate the begin the end positions.
|
27
25
|
def calculate_position
|
28
|
-
|
29
|
-
|
30
|
-
file_content = NodeMutation.adapter.file_content(@node)
|
31
|
-
|
32
|
-
whitespace_index = node_start
|
33
|
-
while file_content[whitespace_index -= 1] == ' '
|
34
|
-
end
|
35
|
-
@start = whitespace_index - Engine::ERUBY_STMT_SPLITTER.length + 1
|
36
|
-
|
37
|
-
at_index = node_start + node_source.index('do')
|
38
|
-
while file_content[at_index += 1] != '@'
|
39
|
-
end
|
40
|
-
@end = at_index
|
26
|
+
@start = NodeMutation.adapter.get_start(@node) - 1
|
27
|
+
@end = @start
|
41
28
|
end
|
42
29
|
end
|
43
30
|
end
|
@@ -51,14 +51,6 @@ module Synvert::Core
|
|
51
51
|
|
52
52
|
process_with_node(node) do
|
53
53
|
instance_eval(&@block)
|
54
|
-
rescue NoMethodError => e
|
55
|
-
puts [
|
56
|
-
"error: #{e.message}",
|
57
|
-
"file: #{file_path}",
|
58
|
-
"source: #{source}",
|
59
|
-
"line: #{current_node.line}"
|
60
|
-
].join("\n")
|
61
|
-
raise
|
62
54
|
end
|
63
55
|
|
64
56
|
result = @current_mutation.process
|
@@ -87,14 +79,6 @@ module Synvert::Core
|
|
87
79
|
|
88
80
|
process_with_node(node) do
|
89
81
|
instance_eval(&@block)
|
90
|
-
rescue NoMethodError => e
|
91
|
-
puts [
|
92
|
-
"error: #{e.message}",
|
93
|
-
"file: #{file_path}",
|
94
|
-
"source: #{source}",
|
95
|
-
"line: #{current_node.line}"
|
96
|
-
].join("\n")
|
97
|
-
raise
|
98
82
|
end
|
99
83
|
|
100
84
|
result = @current_mutation.test
|
@@ -312,7 +296,8 @@ module Synvert::Core
|
|
312
296
|
# replace_erb_stmt_with_expr
|
313
297
|
# end
|
314
298
|
def replace_erb_stmt_with_expr
|
315
|
-
|
299
|
+
action = Rewriter::ReplaceErbStmtWithExprAction.new(@current_node)
|
300
|
+
add_action(action)
|
316
301
|
end
|
317
302
|
|
318
303
|
# It replaces the whole code of current node.
|
@@ -437,16 +422,13 @@ module Synvert::Core
|
|
437
422
|
# @param file_path [String] file path
|
438
423
|
# @return [String] file source
|
439
424
|
def read_source(file_path)
|
440
|
-
|
441
|
-
source = Engine::Erb.encode(source) if /\.erb$/.match?(file_path)
|
442
|
-
source
|
425
|
+
File.read(file_path, encoding: 'UTF-8')
|
443
426
|
end
|
444
427
|
|
445
428
|
# Write file source to file.
|
446
429
|
# @param file_path [String] file path
|
447
430
|
# @param source [String] file source
|
448
431
|
def write_source(file_path, source)
|
449
|
-
source = Engine::Erb.decode(source) if /\.erb/.match?(file_path)
|
450
432
|
File.write(file_path, source.gsub(/ +\n/, "\n"))
|
451
433
|
end
|
452
434
|
|
@@ -457,7 +439,7 @@ module Synvert::Core
|
|
457
439
|
# @return [Node] ast node for file
|
458
440
|
def parse_code(file_path, source)
|
459
441
|
buffer = Parser::Source::Buffer.new file_path
|
460
|
-
buffer.source = source
|
442
|
+
buffer.source = /\.erb/.match?(file_path) ? Engine::Erb.encode(source) : source
|
461
443
|
|
462
444
|
parser = Parser::CurrentRuby.new
|
463
445
|
parser.reset
|
data/lib/synvert/core/version.rb
CHANGED
@@ -6,13 +6,13 @@ module Synvert::Core
|
|
6
6
|
describe Engine::Erb do
|
7
7
|
it 'encodes / decodes' do
|
8
8
|
source = <<~EOF
|
9
|
-
<%content_for :head do%>
|
9
|
+
<% content_for :head do %>
|
10
10
|
<style>
|
11
11
|
body {
|
12
12
|
background-image: url(<%= asset_path('bg.png') %>);
|
13
13
|
}
|
14
14
|
</style>
|
15
|
-
<%end%>
|
15
|
+
<% end %>
|
16
16
|
|
17
17
|
<%
|
18
18
|
foo = 'bar'
|
@@ -34,14 +34,11 @@ module Synvert::Core
|
|
34
34
|
<% end %>
|
35
35
|
<% end %>
|
36
36
|
EOF
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
parser.parse buffer
|
43
|
-
|
44
|
-
expect(Engine::Erb.decode(encoded_source)).to eq source
|
37
|
+
encoded_lines = Engine::Erb.encode(source).split("\n")
|
38
|
+
expect(encoded_lines[0]).to eq ' content_for :head do '
|
39
|
+
expect(encoded_lines[1]).to eq " asset_path('bg.png') "
|
40
|
+
expect(encoded_lines[-2]).to eq ' end '
|
41
|
+
expect(encoded_lines[-1]).to eq ' end '
|
45
42
|
end
|
46
43
|
end
|
47
44
|
end
|
@@ -8,20 +8,20 @@ module Synvert::Core
|
|
8
8
|
subject {
|
9
9
|
source = "<% form_for post do |f| %>\n<% end %>"
|
10
10
|
source = Engine::Erb.encode(source)
|
11
|
-
node = Parser::CurrentRuby.parse(source).children
|
11
|
+
node = Parser::CurrentRuby.parse(source).children.first
|
12
12
|
described_class.new(node).process
|
13
13
|
}
|
14
14
|
|
15
15
|
it 'gets start' do
|
16
|
-
expect(subject.start).to eq '
|
16
|
+
expect(subject.start).to eq '<%'.length
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'gets end' do
|
20
|
-
expect(subject.end).to eq '
|
20
|
+
expect(subject.end).to eq '<%'.length
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'gets new_code' do
|
24
|
-
expect(subject.new_code).to eq '
|
24
|
+
expect(subject.new_code).to eq '='
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -287,12 +287,32 @@ module Synvert::Core
|
|
287
287
|
instance.process
|
288
288
|
expect(rewriter.affected_files).to be_include('spec/models/post_spec.rb')
|
289
289
|
end
|
290
|
+
|
291
|
+
it 'updates erb file' do
|
292
|
+
instance =
|
293
|
+
Rewriter::Instance.new rewriter, 'app/views/posts/_form.html.erb' do
|
294
|
+
with_node type: 'send', receiver: nil, message: 'form_for' do
|
295
|
+
replace_erb_stmt_with_expr
|
296
|
+
end
|
297
|
+
end
|
298
|
+
input = <<~EOS
|
299
|
+
<% form_for @post do |f| %>
|
300
|
+
<% end %>
|
301
|
+
EOS
|
302
|
+
output = <<~EOS
|
303
|
+
<%= form_for @post do |f| %>
|
304
|
+
<% end %>
|
305
|
+
EOS
|
306
|
+
expect(File).to receive(:read).with('./app/views/posts/_form.html.erb', encoding: 'UTF-8').and_return(input)
|
307
|
+
expect(File).to receive(:write).with('./app/views/posts/_form.html.erb', output)
|
308
|
+
instance.process
|
309
|
+
end
|
290
310
|
end
|
291
311
|
|
292
312
|
describe '#test' do
|
293
313
|
let(:rewriter) { Rewriter.new('foo', 'bar') }
|
294
314
|
|
295
|
-
it '
|
315
|
+
it 'gets actions if afected' do
|
296
316
|
instance =
|
297
317
|
Rewriter::Instance.new rewriter, 'spec/models/post_spec.rb' do
|
298
318
|
with_node type: 'send', receiver: 'FactoryGirl', message: 'create' do
|
@@ -315,7 +335,7 @@ module Synvert::Core
|
|
315
335
|
]
|
316
336
|
end
|
317
337
|
|
318
|
-
it '
|
338
|
+
it 'gets nothing if not affected' do
|
319
339
|
instance =
|
320
340
|
Rewriter::Instance.new rewriter, 'spec/spec_helper.rb' do
|
321
341
|
with_node type: 'block', caller: { receiver: 'RSpec', message: 'configure' } do
|
@@ -334,6 +354,23 @@ module Synvert::Core
|
|
334
354
|
expect(result.file_path).to eq 'spec/spec_helper.rb'
|
335
355
|
expect(result.actions).to eq []
|
336
356
|
end
|
357
|
+
|
358
|
+
it 'updates erb file' do
|
359
|
+
instance =
|
360
|
+
Rewriter::Instance.new rewriter, 'app/views/posts/_form.html.erb' do
|
361
|
+
with_node type: 'send', receiver: nil, message: 'form_for' do
|
362
|
+
replace_erb_stmt_with_expr
|
363
|
+
end
|
364
|
+
end
|
365
|
+
input = <<~EOS
|
366
|
+
<% form_for @post do |f| %>
|
367
|
+
<% end %>
|
368
|
+
EOS
|
369
|
+
expect(File).to receive(:read).with('./app/views/posts/_form.html.erb', encoding: 'UTF-8').and_return(input)
|
370
|
+
result = instance.test
|
371
|
+
expect(result.file_path).to eq 'app/views/posts/_form.html.erb'
|
372
|
+
expect(result.actions).to eq [OpenStruct.new(start: 2, end: 2, new_code: '=')]
|
373
|
+
end
|
337
374
|
end
|
338
375
|
|
339
376
|
describe '#process_with_node' do
|
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.21.
|
4
|
+
version: 1.21.5
|
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-02-
|
11
|
+
date: 2023-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|