node_mutation 1.0.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +8 -8
- data/README.md +8 -2
- data/lib/node_mutation/action/wrap_action.rb +1 -1
- data/lib/node_mutation/action.rb +1 -1
- data/lib/node_mutation/engine/erb.rb +148 -0
- data/lib/node_mutation/engine.rb +6 -0
- data/lib/node_mutation/parser_adapter.rb +2 -2
- data/lib/node_mutation/result.rb +19 -0
- data/lib/node_mutation/version.rb +1 -1
- data/lib/node_mutation.rb +26 -24
- data/node_mutation.gemspec +1 -0
- data/sig/node_mutation/result.rbs +9 -0
- data/sig/node_mutation.rbs +3 -3
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16d1f6952b16d815e9cee50df011c99075ffd17bc17531efc8b6a99c39909d4a
|
4
|
+
data.tar.gz: 3f558c1b9e171310775fec6ccdb3a1fe0321aa0ce94b23ec8c861e3ae496dd31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7221a70883e380a9f88dda96db69890349dde212580f63fac75d02a71bb558587d4ccf7e6b33547b0b44846ee493fd7ed924e13eb7cc414b27979c3fb354876e
|
7
|
+
data.tar.gz: 7d1cc7ab27508b202589d22bd6affc07345d19ad12701b7a3aea2d9bc06125a43e17870f787a539a16faaf1ed7db14a13c28b4b8a46d0afaa3b1d2fe701c4ef8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# NodeMutation
|
2
2
|
|
3
|
+
## 1.2.1 (2022-07-22)
|
4
|
+
|
5
|
+
* Fix `child_node_range` for const name
|
6
|
+
* Update `parser_node_ext` to 0.4.0
|
7
|
+
|
8
|
+
## 1.2.0 (2022-07-02)
|
9
|
+
|
10
|
+
* Return new source instead of writing file
|
11
|
+
* Revert Add erb engine
|
12
|
+
* Revert Add `ReplaceErbStmtWithExprAction`
|
13
|
+
|
14
|
+
## 1.1.0 (2022-07-02)
|
15
|
+
|
16
|
+
* Add erb engine
|
17
|
+
* Add `ReplaceErbStmtWithExprAction`
|
18
|
+
|
3
19
|
## 1.0.0 (2022-07-01)
|
4
20
|
|
5
21
|
* Initial release
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
node_mutation (1.
|
4
|
+
node_mutation (1.2.1)
|
5
5
|
activesupport
|
6
|
+
erubis
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
10
|
-
activesupport (7.0.3)
|
11
|
+
activesupport (7.0.3.1)
|
11
12
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
13
|
i18n (>= 1.6, < 2)
|
13
14
|
minitest (>= 5.1)
|
@@ -16,7 +17,7 @@ GEM
|
|
16
17
|
coderay (1.1.3)
|
17
18
|
concurrent-ruby (1.1.10)
|
18
19
|
diff-lcs (1.5.0)
|
19
|
-
|
20
|
+
erubis (2.7.0)
|
20
21
|
ffi (1.15.5)
|
21
22
|
formatador (1.1.0)
|
22
23
|
guard (2.18.0)
|
@@ -33,21 +34,21 @@ GEM
|
|
33
34
|
guard (~> 2.1)
|
34
35
|
guard-compat (~> 1.1)
|
35
36
|
rspec (>= 2.99.0, < 4.0)
|
36
|
-
i18n (1.
|
37
|
+
i18n (1.12.0)
|
37
38
|
concurrent-ruby (~> 1.0)
|
38
39
|
listen (3.7.1)
|
39
40
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
40
41
|
rb-inotify (~> 0.9, >= 0.9.10)
|
41
42
|
lumberjack (1.2.8)
|
42
43
|
method_source (1.0.0)
|
43
|
-
minitest (5.16.
|
44
|
+
minitest (5.16.2)
|
44
45
|
nenv (0.3.0)
|
45
46
|
notiffany (0.1.3)
|
46
47
|
nenv (~> 0.1)
|
47
48
|
shellany (~> 0.0)
|
48
49
|
parser (3.1.2.0)
|
49
50
|
ast (~> 2.4.1)
|
50
|
-
parser_node_ext (0.
|
51
|
+
parser_node_ext (0.4.0)
|
51
52
|
parser
|
52
53
|
pp (0.3.0)
|
53
54
|
prettyprint
|
@@ -74,7 +75,7 @@ GEM
|
|
74
75
|
rspec-support (3.11.0)
|
75
76
|
shellany (0.0.1)
|
76
77
|
thor (1.2.1)
|
77
|
-
tzinfo (2.0.
|
78
|
+
tzinfo (2.0.5)
|
78
79
|
concurrent-ruby (~> 1.0)
|
79
80
|
|
80
81
|
PLATFORMS
|
@@ -82,7 +83,6 @@ PLATFORMS
|
|
82
83
|
x86_64-linux
|
83
84
|
|
84
85
|
DEPENDENCIES
|
85
|
-
fakefs
|
86
86
|
guard
|
87
87
|
guard-rspec
|
88
88
|
node_mutation!
|
data/README.md
CHANGED
@@ -25,7 +25,7 @@ Or install it yourself as:
|
|
25
25
|
```ruby
|
26
26
|
require 'node_mutation'
|
27
27
|
|
28
|
-
mutation = NodeMutation.new(
|
28
|
+
mutation = NodeMutation.new(source)
|
29
29
|
```
|
30
30
|
|
31
31
|
2. call the rewrite apis:
|
@@ -54,7 +54,13 @@ mutation.wrap node, with: 'module Foo'
|
|
54
54
|
3. process actions and write the new source code to file:
|
55
55
|
|
56
56
|
```ruby
|
57
|
-
mutation.process
|
57
|
+
result = mutation.process
|
58
|
+
# if it makes any change to the source
|
59
|
+
result.affected?
|
60
|
+
# if any action is conflicted
|
61
|
+
result.conflicted
|
62
|
+
# return the new source if it is affected
|
63
|
+
result.new_source
|
58
64
|
```
|
59
65
|
|
60
66
|
## Write Adapter
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# WrapAction to wrap node within a block, class or module.
|
4
4
|
#
|
5
|
-
# Note: if WrapAction is conflicted with another action (
|
5
|
+
# Note: if WrapAction is conflicted with another action (start and end are overlapped),
|
6
6
|
# we have to put those 2 actions into 2 within_file scopes.
|
7
7
|
class NodeMutation::WrapAction < NodeMutation::Action
|
8
8
|
# Initialize a WrapAction.
|
data/lib/node_mutation/action.rb
CHANGED
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erubis'
|
4
|
+
|
5
|
+
module NodeMutation::Engine
|
6
|
+
ERUBY_EXPR_SPLITTER = '; ;'
|
7
|
+
ERUBY_STMT_SPLITTER = '; ;'
|
8
|
+
|
9
|
+
class Erb
|
10
|
+
class << self
|
11
|
+
# convert erb to ruby code.
|
12
|
+
#
|
13
|
+
# @param source [String] erb source code.
|
14
|
+
# @return [String] ruby source code.
|
15
|
+
def encode(source)
|
16
|
+
Erubis.new(source.gsub('-%>', '%>'), escape: false, trim: false).src
|
17
|
+
end
|
18
|
+
|
19
|
+
# convert ruby code to erb.
|
20
|
+
#
|
21
|
+
# @param source [String] ruby source code.
|
22
|
+
# @return [String] erb source code.
|
23
|
+
def decode(source)
|
24
|
+
source = decode_ruby_stmt(source)
|
25
|
+
source = decode_ruby_output(source)
|
26
|
+
source = decode_html_output(source)
|
27
|
+
source = remove_erubis_buf(source)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def decode_ruby_stmt(source)
|
33
|
+
source.gsub(/#{ERUBY_STMT_SPLITTER}(.+?)#{ERUBY_STMT_SPLITTER}/mo) { "<%#{Regexp.last_match(1)}%>" }
|
34
|
+
end
|
35
|
+
|
36
|
+
def decode_ruby_output(source)
|
37
|
+
source.gsub(/@output_buffer.append=\((.+?)\);#{ERUBY_EXPR_SPLITTER}/mo) {
|
38
|
+
"<%=#{Regexp.last_match(1)}%>"
|
39
|
+
}.gsub(/@output_buffer.append= (.+?)\s+(do|\{)(\s*\|[^|]*\|)?\s*#{ERUBY_EXPR_SPLITTER}/mo) { |m|
|
40
|
+
"<%=#{m.sub('@output_buffer.append= ', '').sub(ERUBY_EXPR_SPLITTER, '')}%>"
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def decode_html_output(source)
|
45
|
+
source.gsub(/@output_buffer.safe_append='(.+?)'.freeze;/m) { reverse_escape_text(Regexp.last_match(1)) }
|
46
|
+
.gsub(
|
47
|
+
/@output_buffer.safe_append=\((.+?)\);#{ERUBY_EXPR_SPLITTER}/mo
|
48
|
+
) { reverse_escape_text(Regexp.last_match(1)) }
|
49
|
+
.gsub(
|
50
|
+
/@output_buffer.safe_append=(.+?)\s+(do|\{)(\s*\|[^|]*\|)?\s*#{ERUBY_EXPR_SPLITTER}/mo
|
51
|
+
) { reverse_escape_text(Regexp.last_match(1)) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_erubis_buf(source)
|
55
|
+
source
|
56
|
+
.sub('@output_buffer = output_buffer || ActionView::OutputBuffer.new;', '')
|
57
|
+
.sub('@output_buffer.to_s', '')
|
58
|
+
end
|
59
|
+
|
60
|
+
def reverse_escape_text(source)
|
61
|
+
source.gsub("\\\\", "\\").gsub("\\'", "'")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# borrowed from rails
|
67
|
+
class Erubis < ::Erubis::Eruby
|
68
|
+
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
69
|
+
def add_preamble(src)
|
70
|
+
@newline_pending = 0
|
71
|
+
src << '@output_buffer = output_buffer || ActionView::OutputBuffer.new;'
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_text(src, text)
|
75
|
+
return if text.empty?
|
76
|
+
|
77
|
+
if text == "\n"
|
78
|
+
@newline_pending += 1
|
79
|
+
else
|
80
|
+
src << "@output_buffer.safe_append='"
|
81
|
+
src << ("\n" * @newline_pending) if @newline_pending > 0
|
82
|
+
src << escape_text(text)
|
83
|
+
src << "'.freeze;"
|
84
|
+
|
85
|
+
@newline_pending = 0
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Erubis toggles <%= and <%== behavior when escaping is enabled.
|
90
|
+
# We override to always treat <%== as escaped.
|
91
|
+
def add_expr(src, code, indicator)
|
92
|
+
case indicator
|
93
|
+
when '=='
|
94
|
+
add_expr_escaped(src, code)
|
95
|
+
else
|
96
|
+
super
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_expr_literal(src, code)
|
101
|
+
flush_newline_if_pending(src)
|
102
|
+
if BLOCK_EXPR.match?(code)
|
103
|
+
src << '@output_buffer.append= ' << code << ERUBY_EXPR_SPLITTER
|
104
|
+
else
|
105
|
+
src << '@output_buffer.append=(' << code << ');' << ERUBY_EXPR_SPLITTER
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_expr_escaped(src, code)
|
110
|
+
flush_newline_if_pending(src)
|
111
|
+
if BLOCK_EXPR.match?(code)
|
112
|
+
src << '@output_buffer.safe_append= ' << code << ERUBY_EXPR_SPLITTER
|
113
|
+
else
|
114
|
+
src << '@output_buffer.safe_append=(' << code << ');' << ERUBY_EXPR_SPLITTER
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_stmt(src, code)
|
119
|
+
flush_newline_if_pending(src)
|
120
|
+
if code != "\n" && code != ''
|
121
|
+
index =
|
122
|
+
case code
|
123
|
+
when /\A(\s*)\r?\n/
|
124
|
+
Regexp.last_match(1).length
|
125
|
+
when /\A(\s+)/
|
126
|
+
Regexp.last_match(1).end_with?(' ') ? Regexp.last_match(1).length - 1 : Regexp.last_match(1).length
|
127
|
+
else
|
128
|
+
0
|
129
|
+
end
|
130
|
+
code.insert(index, ERUBY_STMT_SPLITTER)
|
131
|
+
code.insert(-1, ERUBY_STMT_SPLITTER[0...-1])
|
132
|
+
end
|
133
|
+
super
|
134
|
+
end
|
135
|
+
|
136
|
+
def add_postamble(src)
|
137
|
+
flush_newline_if_pending(src)
|
138
|
+
src << '@output_buffer.to_s'
|
139
|
+
end
|
140
|
+
|
141
|
+
def flush_newline_if_pending(src)
|
142
|
+
if @newline_pending > 0
|
143
|
+
src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
|
144
|
+
@newline_pending = 0
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -77,7 +77,7 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
|
|
77
77
|
start: node.arguments.first.loc.expression.begin_pos,
|
78
78
|
end: node.arguments.last.loc.expression.end_pos
|
79
79
|
)
|
80
|
-
when %i[class name], %i[def name], %i[defs name]
|
80
|
+
when %i[class name], %i[const name], %i[def name], %i[defs name]
|
81
81
|
OpenStruct.new(start: node.loc.name.begin_pos, end: node.loc.name.end_pos)
|
82
82
|
when %i[defs dot]
|
83
83
|
OpenStruct.new(start: node.loc.operator.begin_pos, end: node.loc.operator.end_pos) if node.loc.operator
|
@@ -171,4 +171,4 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
|
|
171
171
|
return child_node
|
172
172
|
end
|
173
173
|
end
|
174
|
-
end
|
174
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NodeMutation::Result
|
4
|
+
def initialize(options)
|
5
|
+
@options = options
|
6
|
+
end
|
7
|
+
|
8
|
+
def affected?
|
9
|
+
@options[:affected]
|
10
|
+
end
|
11
|
+
|
12
|
+
def conflicted?
|
13
|
+
@options[:conflicted]
|
14
|
+
end
|
15
|
+
|
16
|
+
def new_source
|
17
|
+
@options[:new_source]
|
18
|
+
end
|
19
|
+
end
|
data/lib/node_mutation.rb
CHANGED
@@ -9,7 +9,7 @@ class NodeMutation
|
|
9
9
|
class MethodNotSupported < StandardError; end
|
10
10
|
class ConflictActionError < StandardError; end
|
11
11
|
|
12
|
-
|
12
|
+
KEEP_RUNNING = 1
|
13
13
|
THROW_ERROR = 2
|
14
14
|
|
15
15
|
autoload :Adapter, "node_mutation/adapter"
|
@@ -22,9 +22,9 @@ class NodeMutation
|
|
22
22
|
autoload :RemoveAction, 'node_mutation/action/remove_action'
|
23
23
|
autoload :PrependAction, 'node_mutation/action/prepend_action'
|
24
24
|
autoload :ReplaceAction, 'node_mutation/action/replace_action'
|
25
|
-
autoload :ReplaceErbStmtWithExprAction, 'node_mutation/action/replace_erb_stmt_with_expr_action'
|
26
25
|
autoload :ReplaceWithAction, 'node_mutation/action/replace_with_action'
|
27
26
|
autoload :WrapAction, 'node_mutation/action/wrap_action'
|
27
|
+
autoload :Result, 'node_mutation/result'
|
28
28
|
|
29
29
|
attr_reader :actions
|
30
30
|
|
@@ -47,18 +47,16 @@ class NodeMutation
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# Get the strategy
|
50
|
-
# @return [Integer] current strategy, could be {NodeMutation::
|
51
|
-
# by default is {NodeMutation::
|
50
|
+
# @return [Integer] current strategy, could be {NodeMutation::KEEP_RUNNING} or {NodeMutation::THROW_ERROR},
|
51
|
+
# by default is {NodeMutation::KEEP_RUNNING}
|
52
52
|
def self.strategy
|
53
53
|
@strategy ||= KEEP_RUNNING
|
54
54
|
end
|
55
55
|
|
56
56
|
# Initialize a NodeMutation.
|
57
|
-
# @param
|
58
|
-
|
59
|
-
|
60
|
-
@file_path = file_path
|
61
|
-
@source = +source
|
57
|
+
# @param source [String] file source
|
58
|
+
def initialize(source)
|
59
|
+
@source = source
|
62
60
|
@actions = []
|
63
61
|
end
|
64
62
|
|
@@ -169,8 +167,8 @@ class NodeMutation
|
|
169
167
|
# source code of the ast node is
|
170
168
|
# assert(object.empty?)
|
171
169
|
# then we call
|
172
|
-
# replace :message, with: 'assert_empty'
|
173
|
-
# replace :arguments, with: '{{arguments.first.receiver}}'
|
170
|
+
# mutation.replace(node, :message, with: 'assert_empty')
|
171
|
+
# mutation.replace(node, :arguments, with: '{{arguments.first.receiver}}')
|
174
172
|
# the source code will be rewritten to
|
175
173
|
# assert_empty(object)
|
176
174
|
def replace(node, *selectors, with:)
|
@@ -219,21 +217,25 @@ class NodeMutation
|
|
219
217
|
# if strategy is set to KEEP_RUNNING.
|
220
218
|
# @return {{conflict: Boolean}} if actions are conflicted
|
221
219
|
def process
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
conflict_actions = get_conflict_actions
|
226
|
-
if conflict_actions.size > 0 && NodeMutation.strategy == THROW_ERROR
|
227
|
-
raise ConflictActionError, "mutation actions are conflicted"
|
228
|
-
end
|
229
|
-
@actions.reverse_each do |action|
|
230
|
-
@source[action.start...action.end] = action.new_code
|
231
|
-
end
|
232
|
-
@actions = []
|
220
|
+
if @actions.length == 0
|
221
|
+
return NodeMutation::Result.new(affected: false)
|
222
|
+
end
|
233
223
|
|
234
|
-
|
224
|
+
conflict_actions = []
|
225
|
+
source = +@source
|
226
|
+
@actions.sort_by! { |action| [action.start, action.end] }
|
227
|
+
conflict_actions = get_conflict_actions
|
228
|
+
if conflict_actions.size > 0 && NodeMutation.strategy == THROW_ERROR
|
229
|
+
raise ConflictActionError, "mutation actions are conflicted"
|
230
|
+
end
|
231
|
+
@actions.reverse_each do |action|
|
232
|
+
source[action.start...action.end] = action.new_code
|
235
233
|
end
|
236
|
-
|
234
|
+
NodeMutation::Result.new(
|
235
|
+
affected: true,
|
236
|
+
conflicted: !conflict_actions.empty?,
|
237
|
+
new_source: source
|
238
|
+
)
|
237
239
|
end
|
238
240
|
|
239
241
|
private
|
data/node_mutation.gemspec
CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
|
31
31
|
# Uncomment to register a new dependency of your gem
|
32
32
|
spec.add_dependency "activesupport"
|
33
|
+
spec.add_dependency "erubis"
|
33
34
|
|
34
35
|
# For more information and examples about making a new gem, check out our
|
35
36
|
# guide at: https://bundler.io/guides/creating_gem.html
|
data/sig/node_mutation.rbs
CHANGED
@@ -7,7 +7,7 @@ module NodeMutation[T]
|
|
7
7
|
class ConflictActionError < StandardError
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
KEEP_RUNNING: Integer
|
11
11
|
|
12
12
|
THROW_ERROR: Integer
|
13
13
|
|
@@ -19,7 +19,7 @@ module NodeMutation[T]
|
|
19
19
|
|
20
20
|
def self.strategry: () -> Integer
|
21
21
|
|
22
|
-
def initialize: (
|
22
|
+
def initialize: (source: String) -> NodeMutation
|
23
23
|
|
24
24
|
def append: (node: T, code: String) -> void
|
25
25
|
|
@@ -39,5 +39,5 @@ module NodeMutation[T]
|
|
39
39
|
|
40
40
|
def wrap: (node: T, with: String) -> void
|
41
41
|
|
42
|
-
def process: () ->
|
42
|
+
def process: () -> NodeMutation::Result
|
43
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: node_mutation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-07-
|
11
|
+
date: 2022-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: erubis
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
description: ast node mutation apis
|
28
42
|
email:
|
29
43
|
- flyerhzm@gmail.com
|
@@ -51,11 +65,15 @@ files:
|
|
51
65
|
- lib/node_mutation/action/replace_with_action.rb
|
52
66
|
- lib/node_mutation/action/wrap_action.rb
|
53
67
|
- lib/node_mutation/adapter.rb
|
68
|
+
- lib/node_mutation/engine.rb
|
69
|
+
- lib/node_mutation/engine/erb.rb
|
54
70
|
- lib/node_mutation/parser_adapter.rb
|
71
|
+
- lib/node_mutation/result.rb
|
55
72
|
- lib/node_mutation/version.rb
|
56
73
|
- node_mutation.gemspec
|
57
74
|
- sig/node_mutation.rbs
|
58
75
|
- sig/node_mutation/adapter.rbs
|
76
|
+
- sig/node_mutation/result.rbs
|
59
77
|
homepage: https://github.com/xinminlabs/node-mutation-ruby
|
60
78
|
licenses: []
|
61
79
|
metadata:
|