json-merge 1.1.2 → 7.1.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
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.md +13 -0
- data/README.md +190 -618
- data/lib/json/merge/comment_tracker.rb +27 -0
- data/lib/json/merge/conflict_resolver.rb +1156 -113
- data/lib/json/merge/debug_logger.rb +8 -8
- data/lib/json/merge/emitter.rb +101 -37
- data/lib/json/merge/file_analysis.rb +261 -96
- data/lib/json/merge/freeze_node.rb +62 -0
- data/lib/json/merge/merge_result.rb +25 -5
- data/lib/json/merge/node_wrapper.rb +82 -131
- data/lib/json/merge/object_match_refiner.rb +13 -14
- data/lib/json/merge/smart_merger.rb +136 -10
- data/lib/json/merge/version.rb +5 -4
- data/lib/json/merge.rb +498 -101
- data/lib/json-merge.rb +7 -4
- data/sig/json/merge.rbs +3 -196
- data.tar.gz.sig +0 -0
- metadata +99 -81
- metadata.gz.sig +0 -0
- data/CHANGELOG.md +0 -149
- data/CITATION.cff +0 -20
- data/CODE_OF_CONDUCT.md +0 -134
- data/CONTRIBUTING.md +0 -227
- data/FUNDING.md +0 -74
- data/LICENSE.txt +0 -21
- data/REEK +0 -0
- data/RUBOCOP.md +0 -71
- data/SECURITY.md +0 -21
|
@@ -15,23 +15,23 @@ module Json
|
|
|
15
15
|
extend Ast::Merge::DebugLogger
|
|
16
16
|
|
|
17
17
|
# Json-specific configuration
|
|
18
|
-
self.env_var_name =
|
|
19
|
-
self.log_prefix =
|
|
18
|
+
self.env_var_name = 'JSON_MERGE_DEBUG'
|
|
19
|
+
self.log_prefix = '[Json::Merge]'
|
|
20
20
|
|
|
21
21
|
class << self
|
|
22
22
|
# Override log_node to handle Json-specific node types.
|
|
23
23
|
#
|
|
24
24
|
# @param node [Object] Node to log information about
|
|
25
25
|
# @param label [String] Label for the node
|
|
26
|
-
def log_node(node, label:
|
|
26
|
+
def log_node(node, label: 'Node')
|
|
27
27
|
return unless enabled?
|
|
28
28
|
|
|
29
29
|
info = case node
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
when Json::Merge::NodeWrapper
|
|
31
|
+
{ type: node.type.to_s, lines: "#{node.start_line}..#{node.end_line}" }
|
|
32
|
+
else
|
|
33
|
+
extract_node_info(node)
|
|
34
|
+
end
|
|
35
35
|
|
|
36
36
|
debug(label, info)
|
|
37
37
|
end
|
data/lib/json/merge/emitter.rb
CHANGED
|
@@ -14,28 +14,36 @@ module Json
|
|
|
14
14
|
# emitter.emit_pair("key", '"value"')
|
|
15
15
|
# emitter.emit_object_end
|
|
16
16
|
class Emitter < Ast::Merge::EmitterBase
|
|
17
|
+
include Ast::Merge::EmitterLineMetadataSupport
|
|
18
|
+
|
|
17
19
|
# @return [Boolean] Whether next item needs a comma
|
|
18
20
|
attr_reader :needs_comma
|
|
19
21
|
|
|
20
22
|
# Initialize subclass-specific state (comma tracking for JSON)
|
|
21
|
-
def initialize_subclass_state(**
|
|
23
|
+
def initialize_subclass_state(**_options)
|
|
22
24
|
@needs_comma = false
|
|
25
|
+
initialize_line_metadata_state
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
# Clear subclass-specific state
|
|
26
29
|
def clear_subclass_state
|
|
27
30
|
@needs_comma = false
|
|
31
|
+
clear_line_metadata_state
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def emit_blank_line
|
|
35
|
+
append_line('')
|
|
28
36
|
end
|
|
29
37
|
|
|
30
38
|
# Emit a tracked comment from CommentTracker
|
|
31
39
|
# @param comment [Hash] Comment with :text, :indent, :block
|
|
32
40
|
def emit_tracked_comment(comment)
|
|
33
|
-
indent =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
indent = ' ' * (comment[:indent] || 0)
|
|
42
|
+
append_line(if comment[:block]
|
|
43
|
+
"#{indent}/* #{comment[:text]} */"
|
|
44
|
+
else
|
|
45
|
+
"#{indent}// #{comment[:text]}"
|
|
46
|
+
end)
|
|
39
47
|
end
|
|
40
48
|
|
|
41
49
|
# Emit a single-line comment
|
|
@@ -49,7 +57,7 @@ module Json
|
|
|
49
57
|
|
|
50
58
|
@lines[-1] = "#{@lines[-1]} // #{text}"
|
|
51
59
|
else
|
|
52
|
-
|
|
60
|
+
append_line("#{current_indent}// #{text}")
|
|
53
61
|
end
|
|
54
62
|
end
|
|
55
63
|
|
|
@@ -57,42 +65,45 @@ module Json
|
|
|
57
65
|
#
|
|
58
66
|
# @param text [String] Comment text
|
|
59
67
|
def emit_block_comment(text)
|
|
60
|
-
|
|
68
|
+
append_line("#{current_indent}/* #{text} */")
|
|
61
69
|
end
|
|
62
70
|
|
|
63
71
|
# Emit object start
|
|
64
|
-
def emit_object_start
|
|
72
|
+
def emit_object_start(metadata: nil)
|
|
65
73
|
add_comma_if_needed
|
|
66
|
-
|
|
74
|
+
append_line("#{current_indent}{", metadata)
|
|
67
75
|
indent
|
|
68
76
|
@needs_comma = false
|
|
69
77
|
end
|
|
70
78
|
|
|
71
79
|
# Emit object end
|
|
72
|
-
def emit_object_end
|
|
80
|
+
def emit_object_end(metadata: nil)
|
|
73
81
|
dedent
|
|
74
|
-
|
|
82
|
+
append_line("#{current_indent}}", metadata)
|
|
75
83
|
@needs_comma = true
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
# Emit array start
|
|
79
87
|
#
|
|
80
88
|
# @param key [String, nil] Key name if this array is a value in an object
|
|
81
|
-
|
|
89
|
+
# @param inline_comment [String, nil] Optional inline comment for the opening line
|
|
90
|
+
def emit_array_start(key = nil, inline_comment: nil, metadata: nil)
|
|
82
91
|
add_comma_if_needed
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
line = if key
|
|
93
|
+
"#{current_indent}\"#{key}\": ["
|
|
94
|
+
else
|
|
95
|
+
"#{current_indent}["
|
|
96
|
+
end
|
|
97
|
+
line += " // #{inline_comment}" if inline_comment
|
|
98
|
+
append_line(line, metadata)
|
|
88
99
|
indent
|
|
89
100
|
@needs_comma = false
|
|
90
101
|
end
|
|
91
102
|
|
|
92
103
|
# Emit array end
|
|
93
|
-
def emit_array_end
|
|
104
|
+
def emit_array_end(metadata: nil)
|
|
94
105
|
dedent
|
|
95
|
-
|
|
106
|
+
append_line("#{current_indent}]", metadata)
|
|
96
107
|
@needs_comma = true
|
|
97
108
|
end
|
|
98
109
|
|
|
@@ -101,11 +112,11 @@ module Json
|
|
|
101
112
|
# @param key [String] Key name (without quotes)
|
|
102
113
|
# @param value [String] Value (already formatted, e.g., '"string"', '123', 'true')
|
|
103
114
|
# @param inline_comment [String, nil] Optional inline comment
|
|
104
|
-
def emit_pair(key, value, inline_comment: nil)
|
|
115
|
+
def emit_pair(key, value, inline_comment: nil, metadata: nil)
|
|
105
116
|
add_comma_if_needed
|
|
106
117
|
line = "#{current_indent}\"#{key}\": #{value}"
|
|
107
118
|
line += " // #{inline_comment}" if inline_comment
|
|
108
|
-
|
|
119
|
+
append_line(line, metadata)
|
|
109
120
|
@needs_comma = true
|
|
110
121
|
end
|
|
111
122
|
|
|
@@ -113,34 +124,57 @@ module Json
|
|
|
113
124
|
#
|
|
114
125
|
# @param value [String] Value (already formatted)
|
|
115
126
|
# @param inline_comment [String, nil] Optional inline comment
|
|
116
|
-
def emit_array_element(value, inline_comment: nil)
|
|
127
|
+
def emit_array_element(value, inline_comment: nil, metadata: nil)
|
|
117
128
|
add_comma_if_needed
|
|
118
129
|
line = "#{current_indent}#{value}"
|
|
119
130
|
line += " // #{inline_comment}" if inline_comment
|
|
120
|
-
|
|
131
|
+
append_line(line, metadata)
|
|
121
132
|
@needs_comma = true
|
|
122
133
|
end
|
|
123
134
|
|
|
124
135
|
# Emit a key with opening brace for nested object
|
|
125
136
|
# @param key [String] Key name
|
|
126
|
-
|
|
137
|
+
# @param inline_comment [String, nil] Optional inline comment for the opening line
|
|
138
|
+
def emit_nested_object_start(key, inline_comment: nil, metadata: nil)
|
|
127
139
|
add_comma_if_needed
|
|
128
|
-
|
|
140
|
+
line = "#{current_indent}\"#{key}\": {"
|
|
141
|
+
line += " // #{inline_comment}" if inline_comment
|
|
142
|
+
append_line(line, metadata)
|
|
129
143
|
indent
|
|
130
144
|
@needs_comma = false
|
|
131
145
|
end
|
|
132
146
|
|
|
133
147
|
# Emit closing brace for nested object
|
|
134
|
-
def emit_nested_object_end
|
|
148
|
+
def emit_nested_object_end(metadata: nil)
|
|
135
149
|
dedent
|
|
136
|
-
|
|
150
|
+
append_line("#{current_indent}}", metadata)
|
|
151
|
+
@needs_comma = true
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def emit_raw_lines(raw_lines, metadata: nil)
|
|
155
|
+
raw_lines.each_with_index do |line, idx|
|
|
156
|
+
append_line(
|
|
157
|
+
line.chomp,
|
|
158
|
+
expanded_line_metadata(metadata, idx)
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def emit_raw_fragment(fragment, metadata: nil)
|
|
164
|
+
add_comma_if_needed
|
|
165
|
+
fragment.to_s.lines(chomp: true).each_with_index do |line, idx|
|
|
166
|
+
append_line(
|
|
167
|
+
"#{current_indent}#{line}",
|
|
168
|
+
expanded_line_metadata(metadata, idx)
|
|
169
|
+
)
|
|
170
|
+
end
|
|
137
171
|
@needs_comma = true
|
|
138
172
|
end
|
|
139
173
|
|
|
140
174
|
# Get the output as a JSON string
|
|
141
175
|
#
|
|
142
176
|
# @return [String]
|
|
143
|
-
def to_json
|
|
177
|
+
def to_json(*_args)
|
|
144
178
|
to_s
|
|
145
179
|
end
|
|
146
180
|
|
|
@@ -149,14 +183,44 @@ module Json
|
|
|
149
183
|
def add_comma_if_needed
|
|
150
184
|
return unless @needs_comma && @lines.any?
|
|
151
185
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
186
|
+
line_index = @lines.length - 1
|
|
187
|
+
while line_index >= 0
|
|
188
|
+
line = @lines[line_index]
|
|
189
|
+
stripped = line.strip
|
|
190
|
+
if stripped.empty? || comment_line?(stripped)
|
|
191
|
+
line_index -= 1
|
|
192
|
+
next
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
break
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
return if line_index.negative?
|
|
199
|
+
|
|
200
|
+
# Add comma to the previous structural line if it doesn't already have one
|
|
201
|
+
last_line = @lines[line_index]
|
|
202
|
+
@lines[line_index] = add_comma_to_line(last_line)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def comment_line?(stripped_line)
|
|
206
|
+
stripped_line.start_with?('//', '/*', '*', '*/')
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def add_comma_to_line(line)
|
|
210
|
+
return line if line.strip.empty?
|
|
211
|
+
|
|
212
|
+
inline_match = line.match(%r{\A(?<content>.*?)(?<spacing>\s+)(?<comment>//.*)\z})
|
|
213
|
+
if inline_match
|
|
214
|
+
content = inline_match[:content].rstrip
|
|
215
|
+
return line if content.end_with?(',', '{', '[')
|
|
216
|
+
|
|
217
|
+
return "#{content}, #{inline_match[:comment]}"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
stripped = line.rstrip
|
|
221
|
+
return line if stripped.end_with?(',', '{', '[')
|
|
158
222
|
|
|
159
|
-
|
|
223
|
+
"#{line},"
|
|
160
224
|
end
|
|
161
225
|
end
|
|
162
226
|
end
|