typescript-merge 7.0.0 → 7.1.3
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 +286 -0
- data/lib/typescript/merge/file_analysis.rb +78 -0
- data/lib/typescript/merge/node_wrapper.rb +224 -0
- data/lib/typescript/merge/provider.rb +953 -0
- data/lib/typescript/merge/version.rb +5 -3
- data/lib/typescript/merge.rb +149 -56
- data/lib/typescript-merge.rb +7 -1
- data/sig/typescript/merge.rbs +54 -0
- data.tar.gz.sig +0 -0
- metadata +255 -15
- metadata.gz.sig +0 -0
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module TypeScript
|
|
4
4
|
module Merge
|
|
5
|
+
# Version namespace for this gem.
|
|
5
6
|
module Version
|
|
6
|
-
|
|
7
|
+
# Current gem version.
|
|
8
|
+
VERSION = '7.1.3'
|
|
7
9
|
end
|
|
8
|
-
|
|
9
|
-
VERSION = Version::VERSION
|
|
10
|
+
# Current gem version exposed at the traditional constant location.
|
|
11
|
+
VERSION = Version::VERSION # Traditional Constant Location
|
|
10
12
|
end
|
|
11
13
|
end
|
data/lib/typescript/merge.rb
CHANGED
|
@@ -1,27 +1,58 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require 'tree_haver'
|
|
4
|
+
require 'ast/merge'
|
|
5
|
+
require_relative 'merge/version'
|
|
5
6
|
|
|
6
7
|
module TypeScript
|
|
7
8
|
module Merge
|
|
8
|
-
|
|
9
|
+
module_function
|
|
9
10
|
|
|
10
|
-
PACKAGE_NAME =
|
|
11
|
+
PACKAGE_NAME = 'typescript-merge'
|
|
11
12
|
TREE_SITTER_BACKEND = TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND
|
|
12
|
-
DESTINATION_WINS_ARRAY_POLICY = { surface:
|
|
13
|
+
DESTINATION_WINS_ARRAY_POLICY = { surface: 'array', name: 'destination_wins_array' }.freeze
|
|
14
|
+
BACKEND_REGISTRY = Struct.new(:registered, :mutex).new(false, Mutex.new)
|
|
15
|
+
|
|
16
|
+
autoload :NodeWrapper, 'typescript/merge/node_wrapper'
|
|
17
|
+
autoload :FileAnalysis, 'typescript/merge/file_analysis'
|
|
18
|
+
autoload :Provider, 'typescript/merge/provider'
|
|
19
|
+
|
|
20
|
+
def register_backend!
|
|
21
|
+
BACKEND_REGISTRY.mutex.synchronize do
|
|
22
|
+
return if BACKEND_REGISTRY.registered
|
|
23
|
+
|
|
24
|
+
TreeHaver::BackendRegistry.register(TREE_SITTER_BACKEND)
|
|
25
|
+
|
|
26
|
+
grammar_finder = TreeHaver::GrammarFinder.new(:typescript)
|
|
27
|
+
grammar_finder.register! if grammar_finder.available?
|
|
28
|
+
TreeHaver.register_language(
|
|
29
|
+
:tsx,
|
|
30
|
+
backend_module: TreeHaver::Backends::Tslp,
|
|
31
|
+
backend_type: :tslp,
|
|
32
|
+
gem_name: 'tree_sitter_language_pack'
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
BACKEND_REGISTRY.registered = true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
13
38
|
|
|
14
39
|
def type_script_feature_profile
|
|
15
|
-
{
|
|
40
|
+
{
|
|
41
|
+
family: 'typescript',
|
|
42
|
+
supported_dialects: %w[typescript tsx],
|
|
43
|
+
supported_policies: [DESTINATION_WINS_ARRAY_POLICY]
|
|
44
|
+
}
|
|
16
45
|
end
|
|
17
46
|
|
|
18
47
|
def available_type_script_backends
|
|
19
|
-
[TREE_SITTER_BACKEND]
|
|
48
|
+
type_script_backend_available_for_analysis?(TREE_SITTER_BACKEND.id) ? [TREE_SITTER_BACKEND] : []
|
|
20
49
|
end
|
|
21
50
|
|
|
22
51
|
def type_script_backend_feature_profile(backend: nil)
|
|
23
|
-
requested = backend
|
|
24
|
-
|
|
52
|
+
requested = requested_tree_sitter_backend_id(backend)
|
|
53
|
+
unless type_script_backend_available_for_analysis?(requested)
|
|
54
|
+
return unsupported_feature_result("Unsupported TypeScript backend #{requested}.")
|
|
55
|
+
end
|
|
25
56
|
|
|
26
57
|
type_script_feature_profile.merge(
|
|
27
58
|
backend: requested,
|
|
@@ -45,101 +76,163 @@ module TypeScript
|
|
|
45
76
|
end
|
|
46
77
|
|
|
47
78
|
def parse_type_script(source, dialect)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
unless type_script_backend_available_for_analysis?(nil)
|
|
80
|
+
diagnostic_backend = TreeHaver.current_backend_id || 'tree-sitter'
|
|
81
|
+
return unsupported_feature_result("Unsupported TypeScript backend #{diagnostic_backend}.")
|
|
82
|
+
end
|
|
83
|
+
return analyze_type_script_module(source, dialect: dialect) if %w[typescript tsx].include?(dialect)
|
|
84
|
+
|
|
85
|
+
{ ok: false,
|
|
86
|
+
diagnostics: [{ severity: 'error', category: 'unsupported_feature', message: "Unsupported TypeScript dialect #{dialect}." }], policies: [] }
|
|
87
|
+
end
|
|
51
88
|
|
|
52
|
-
|
|
89
|
+
def type_script_backend_available_for_analysis?(backend_id)
|
|
90
|
+
register_backend!
|
|
91
|
+
|
|
92
|
+
if backend_id.to_s.empty?
|
|
93
|
+
TreeHaver.parser_for(:typescript, backend_type: :tree_sitter)
|
|
94
|
+
else
|
|
95
|
+
TreeHaver.with_backend(backend_id) { TreeHaver.parser_for(:typescript, backend_type: :tree_sitter) }
|
|
96
|
+
end
|
|
97
|
+
true
|
|
98
|
+
rescue TreeHaver::Error, ArgumentError
|
|
99
|
+
false
|
|
53
100
|
end
|
|
54
101
|
|
|
102
|
+
def requested_tree_sitter_backend_id(backend)
|
|
103
|
+
return backend.to_s unless backend.to_s.empty?
|
|
104
|
+
|
|
105
|
+
contextual = TreeHaver.current_backend_id || ENV['TREE_HAVER_BACKEND']
|
|
106
|
+
contextual.to_s.empty? || contextual.to_s == 'auto' ? TREE_SITTER_BACKEND.id : contextual.to_s
|
|
107
|
+
end
|
|
108
|
+
private_class_method :requested_tree_sitter_backend_id
|
|
109
|
+
|
|
55
110
|
def match_type_script_owners(template, destination)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
111
|
+
Ast::Merge::OwnerSelection.match_by_path(template, destination)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def merge_provider
|
|
115
|
+
@merge_provider ||= Provider.new
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def register_provider!(replace: false)
|
|
119
|
+
return unless Ast::Merge.respond_to?(:register_provider)
|
|
120
|
+
|
|
121
|
+
Ast::Merge.register_provider(merge_provider, replace: replace)
|
|
63
122
|
end
|
|
64
123
|
|
|
65
124
|
def merge_type_script(template_source, destination_source, dialect)
|
|
66
125
|
template = parse_type_script(template_source, dialect)
|
|
67
126
|
return { ok: false, diagnostics: template[:diagnostics], policies: [] } unless template[:ok]
|
|
127
|
+
|
|
68
128
|
destination = parse_type_script(destination_source, dialect)
|
|
69
129
|
unless destination[:ok]
|
|
70
130
|
return {
|
|
71
131
|
ok: false,
|
|
72
|
-
diagnostics: destination[:diagnostics].map
|
|
132
|
+
diagnostics: destination[:diagnostics].map do |diagnostic|
|
|
133
|
+
diagnostic[:category] == 'parse_error' ? diagnostic.merge(category: 'destination_parse_error') : diagnostic
|
|
134
|
+
end,
|
|
73
135
|
policies: []
|
|
74
136
|
}
|
|
75
137
|
end
|
|
76
138
|
|
|
77
139
|
destination_declarations = destination.dig(:analysis, :declarations).to_h { |item| [item[:path], item] }
|
|
78
140
|
merged_declaration_texts = destination.dig(:analysis, :declarations).map { |item| item[:text] } +
|
|
79
|
-
|
|
141
|
+
template.dig(:analysis, :declarations).reject do |item|
|
|
142
|
+
destination_declarations[item[:path]]
|
|
143
|
+
end.map { |item| item[:text] }
|
|
80
144
|
import_block = destination.dig(:analysis, :imports).map { |item| item[:text] }.join
|
|
81
145
|
declaration_block = merged_declaration_texts.join("\n").rstrip
|
|
82
146
|
sections = [import_block.rstrip, declaration_block].reject(&:empty?)
|
|
83
147
|
|
|
84
|
-
{ ok: true, diagnostics: [], output: "#{sections.join("\n\n").rstrip}\n",
|
|
148
|
+
{ ok: true, diagnostics: [], output: "#{sections.join("\n\n").rstrip}\n",
|
|
149
|
+
policies: [DESTINATION_WINS_ARRAY_POLICY] }
|
|
85
150
|
end
|
|
86
151
|
|
|
87
|
-
def analyze_type_script_module(source)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
152
|
+
def analyze_type_script_module(source, dialect: 'typescript')
|
|
153
|
+
analysis = FileAnalysis.new(source, dialect: dialect)
|
|
154
|
+
raise analysis.errors.first unless analysis.errors.empty?
|
|
155
|
+
|
|
156
|
+
imports = []
|
|
157
|
+
declarations = []
|
|
158
|
+
analysis.declarations.each do |wrapper|
|
|
159
|
+
signature = wrapper.signature
|
|
160
|
+
if signature.first == :import
|
|
161
|
+
imports << {
|
|
162
|
+
path: "/imports/#{imports.length}",
|
|
163
|
+
owner_kind: 'import',
|
|
164
|
+
match_key: legacy_import_key(signature),
|
|
165
|
+
text: "#{wrapper.source_text}\n"
|
|
166
|
+
}
|
|
167
|
+
else
|
|
168
|
+
name = legacy_declaration_key(signature)
|
|
169
|
+
declarations << {
|
|
170
|
+
path: "/declarations/#{name}",
|
|
171
|
+
owner_kind: 'declaration',
|
|
172
|
+
match_key: name,
|
|
173
|
+
text: "#{wrapper.source_text}\n"
|
|
174
|
+
}
|
|
175
|
+
end
|
|
95
176
|
end
|
|
96
|
-
declarations = processed[:analysis].structure
|
|
97
|
-
.select { |item| item.name }
|
|
98
|
-
.map { |item| { path: "/declarations/#{item.name}", match_key: item.name, text: declaration_text(source, item.span) } }
|
|
99
|
-
.sort_by { |item| item[:path] }
|
|
100
177
|
|
|
101
178
|
{
|
|
102
179
|
ok: true,
|
|
103
180
|
diagnostics: [],
|
|
104
181
|
analysis: {
|
|
105
|
-
kind:
|
|
106
|
-
dialect:
|
|
107
|
-
source: source,
|
|
108
|
-
owners: imports.map { |item| { path: item[:path], owner_kind: "import", match_key: item[:match_key] } } +
|
|
109
|
-
declarations.map { |item| { path: item[:path], owner_kind: "declaration", match_key: item[:match_key] } },
|
|
182
|
+
kind: 'typescript',
|
|
183
|
+
dialect: dialect,
|
|
110
184
|
imports: imports,
|
|
111
|
-
declarations: declarations
|
|
185
|
+
declarations: declarations,
|
|
186
|
+
owners: owner_views(imports + declarations)
|
|
112
187
|
},
|
|
113
188
|
policies: []
|
|
114
189
|
}
|
|
190
|
+
rescue TreeHaver::Error, StandardError => e
|
|
191
|
+
parse_failure_result(e)
|
|
115
192
|
end
|
|
116
193
|
private_class_method :analyze_type_script_module
|
|
117
194
|
|
|
118
|
-
def
|
|
119
|
-
|
|
195
|
+
def legacy_declaration_key(signature)
|
|
196
|
+
nested = signature.first == :export_declaration ? signature.fetch(1) : signature
|
|
197
|
+
value = nested.last
|
|
198
|
+
value.is_a?(Array) ? value.join(',') : value
|
|
199
|
+
end
|
|
200
|
+
private_class_method :legacy_declaration_key
|
|
201
|
+
|
|
202
|
+
def legacy_import_key(signature)
|
|
203
|
+
literal = signature.fetch(1)
|
|
204
|
+
literal.byteslice(1...-1).to_s
|
|
120
205
|
end
|
|
121
|
-
private_class_method :
|
|
206
|
+
private_class_method :legacy_import_key
|
|
122
207
|
|
|
123
|
-
def
|
|
124
|
-
|
|
208
|
+
def parse_failure_result(error)
|
|
209
|
+
{ ok: false,
|
|
210
|
+
diagnostics: [{ severity: 'error', category: 'parse_error', message: error.message }],
|
|
211
|
+
policies: [] }
|
|
125
212
|
end
|
|
126
|
-
private_class_method :
|
|
213
|
+
private_class_method :parse_failure_result
|
|
127
214
|
|
|
128
|
-
def
|
|
129
|
-
|
|
215
|
+
def owner_view(item)
|
|
216
|
+
item.slice(:path, :owner_kind, :match_key)
|
|
130
217
|
end
|
|
131
|
-
private_class_method :
|
|
218
|
+
private_class_method :owner_view
|
|
132
219
|
|
|
133
|
-
def
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
source[line_start...span.end_byte].strip
|
|
220
|
+
def owner_views(items)
|
|
221
|
+
imports, declarations = items.partition { |item| item.fetch(:owner_kind) == 'import' }
|
|
222
|
+
(imports + declarations.sort_by { |item| item.fetch(:path) }).map { |item| owner_view(item) }
|
|
137
223
|
end
|
|
138
|
-
private_class_method :
|
|
224
|
+
private_class_method :owner_views
|
|
139
225
|
|
|
140
226
|
def unsupported_feature_result(message)
|
|
141
|
-
|
|
227
|
+
{
|
|
228
|
+
ok: false,
|
|
229
|
+
diagnostics: [{ severity: 'error', category: 'unsupported_feature', message: message }],
|
|
230
|
+
policies: []
|
|
231
|
+
}
|
|
142
232
|
end
|
|
143
233
|
private_class_method :unsupported_feature_result
|
|
144
234
|
end
|
|
145
235
|
end
|
|
236
|
+
|
|
237
|
+
TypeScript::Merge.register_backend!
|
|
238
|
+
TypeScript::Merge.register_provider!
|
data/lib/typescript-merge.rb
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module TypeScript
|
|
2
|
+
module Merge
|
|
3
|
+
class NodeWrapper
|
|
4
|
+
DECLARATION_TYPES: Array[String]
|
|
5
|
+
NAMED_DECLARATIONS: Hash[String, [Symbol, Array[String]]]
|
|
6
|
+
|
|
7
|
+
attr_reader node: untyped
|
|
8
|
+
attr_reader source: String
|
|
9
|
+
attr_reader leading_comments: Array[untyped]
|
|
10
|
+
attr_reader trailing_comments: Array[untyped]
|
|
11
|
+
|
|
12
|
+
def initialize: (untyped node, source: String, ?leading_comments: Array[untyped]) -> void
|
|
13
|
+
def signature: () -> Array[untyped]
|
|
14
|
+
def membership_signature: () -> Array[untyped]
|
|
15
|
+
def grouped?: () -> bool
|
|
16
|
+
def start_byte: () -> Integer
|
|
17
|
+
def end_byte: () -> Integer
|
|
18
|
+
def start_line: () -> Integer
|
|
19
|
+
def end_line: () -> Integer
|
|
20
|
+
def source_text: () -> String
|
|
21
|
+
def semantic_tree: () -> Array[untyped]
|
|
22
|
+
def attach_trailing_comment!: (untyped comment) -> untyped
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class FileAnalysis
|
|
26
|
+
attr_reader source: String
|
|
27
|
+
attr_reader root_node: untyped
|
|
28
|
+
attr_reader declarations: Array[NodeWrapper]
|
|
29
|
+
attr_reader errors: Array[StandardError]
|
|
30
|
+
attr_reader backend: Symbol
|
|
31
|
+
attr_reader dialect: Symbol
|
|
32
|
+
|
|
33
|
+
def initialize: (String source, ?dialect: Symbol) -> void
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Provider
|
|
37
|
+
def provider_id: () -> String
|
|
38
|
+
def family: () -> String
|
|
39
|
+
def capabilities: () -> Hash[Symbol, untyped]
|
|
40
|
+
def analyze: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
|
|
41
|
+
def diff2: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
|
|
42
|
+
def merge2: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
|
|
43
|
+
def merge3: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.merge_provider: () -> Provider
|
|
47
|
+
def self.register_provider!: (?replace: bool) -> untyped
|
|
48
|
+
|
|
49
|
+
module Version
|
|
50
|
+
VERSION: String
|
|
51
|
+
end
|
|
52
|
+
VERSION: String
|
|
53
|
+
end
|
|
54
|
+
end
|
data.tar.gz.sig
CHANGED
|
Binary file
|