trellis 2.0.13 → 2.1.2
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.
- package/dist/cli/index.js +1 -1
- package/dist/embeddings/index.js +1 -1
- package/dist/{index-7gvjxt27.js → index-2917tjd8.js} +1 -1
- package/package.json +2 -10
- package/dist/transformers.node-bx3q9d7k.js +0 -33130
- package/src/cli/index.ts +0 -3356
- package/src/core/agents/harness.ts +0 -380
- package/src/core/agents/index.ts +0 -18
- package/src/core/agents/types.ts +0 -90
- package/src/core/index.ts +0 -118
- package/src/core/kernel/middleware.ts +0 -44
- package/src/core/kernel/trellis-kernel.ts +0 -593
- package/src/core/ontology/builtins.ts +0 -248
- package/src/core/ontology/index.ts +0 -34
- package/src/core/ontology/registry.ts +0 -209
- package/src/core/ontology/types.ts +0 -124
- package/src/core/ontology/validator.ts +0 -382
- package/src/core/persist/backend.ts +0 -74
- package/src/core/persist/sqlite-backend.ts +0 -298
- package/src/core/plugins/index.ts +0 -17
- package/src/core/plugins/registry.ts +0 -322
- package/src/core/plugins/types.ts +0 -126
- package/src/core/query/datalog.ts +0 -188
- package/src/core/query/engine.ts +0 -370
- package/src/core/query/index.ts +0 -34
- package/src/core/query/parser.ts +0 -481
- package/src/core/query/types.ts +0 -200
- package/src/core/store/eav-store.ts +0 -467
- package/src/decisions/auto-capture.ts +0 -136
- package/src/decisions/hooks.ts +0 -163
- package/src/decisions/index.ts +0 -261
- package/src/decisions/types.ts +0 -103
- package/src/embeddings/auto-embed.ts +0 -248
- package/src/embeddings/chunker.ts +0 -327
- package/src/embeddings/index.ts +0 -48
- package/src/embeddings/model.ts +0 -112
- package/src/embeddings/search.ts +0 -305
- package/src/embeddings/store.ts +0 -313
- package/src/embeddings/types.ts +0 -92
- package/src/engine.ts +0 -1125
- package/src/garden/cluster.ts +0 -330
- package/src/garden/garden.ts +0 -306
- package/src/garden/index.ts +0 -29
- package/src/git/git-exporter.ts +0 -286
- package/src/git/git-importer.ts +0 -329
- package/src/git/git-reader.ts +0 -189
- package/src/git/index.ts +0 -22
- package/src/identity/governance.ts +0 -211
- package/src/identity/identity.ts +0 -224
- package/src/identity/index.ts +0 -30
- package/src/identity/signing-middleware.ts +0 -97
- package/src/index.ts +0 -29
- package/src/links/index.ts +0 -49
- package/src/links/lifecycle.ts +0 -400
- package/src/links/parser.ts +0 -484
- package/src/links/ref-index.ts +0 -186
- package/src/links/resolver.ts +0 -314
- package/src/links/types.ts +0 -108
- package/src/mcp/index.ts +0 -22
- package/src/mcp/server.ts +0 -1278
- package/src/semantic/csharp-parser.ts +0 -493
- package/src/semantic/go-parser.ts +0 -585
- package/src/semantic/index.ts +0 -34
- package/src/semantic/java-parser.ts +0 -456
- package/src/semantic/python-parser.ts +0 -659
- package/src/semantic/ruby-parser.ts +0 -446
- package/src/semantic/rust-parser.ts +0 -784
- package/src/semantic/semantic-merge.ts +0 -210
- package/src/semantic/ts-parser.ts +0 -681
- package/src/semantic/types.ts +0 -175
- package/src/sync/http-transport.ts +0 -144
- package/src/sync/index.ts +0 -43
- package/src/sync/memory-transport.ts +0 -66
- package/src/sync/multi-repo.ts +0 -200
- package/src/sync/reconciler.ts +0 -237
- package/src/sync/sync-engine.ts +0 -258
- package/src/sync/types.ts +0 -104
- package/src/sync/ws-transport.ts +0 -145
- package/src/ui/client.html +0 -695
- package/src/ui/server.ts +0 -419
- package/src/vcs/blob-store.ts +0 -124
- package/src/vcs/branch.ts +0 -150
- package/src/vcs/checkpoint.ts +0 -64
- package/src/vcs/decompose.ts +0 -469
- package/src/vcs/diff.ts +0 -409
- package/src/vcs/engine-context.ts +0 -26
- package/src/vcs/index.ts +0 -23
- package/src/vcs/issue.ts +0 -800
- package/src/vcs/merge.ts +0 -425
- package/src/vcs/milestone.ts +0 -124
- package/src/vcs/ops.ts +0 -59
- package/src/vcs/types.ts +0 -213
- package/src/vcs/vcs-middleware.ts +0 -81
- package/src/watcher/fs-watcher.ts +0 -255
- package/src/watcher/index.ts +0 -9
- package/src/watcher/ingestion.ts +0 -116
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ruby Parser Adapter
|
|
3
|
-
*
|
|
4
|
-
* Tier 1 regex-based parser for Ruby source files.
|
|
5
|
-
* Extracts classes, modules, methods, constants, attributes,
|
|
6
|
-
* require/include statements, and module_function exports.
|
|
7
|
-
*
|
|
8
|
-
* @see TRL-8
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type {
|
|
12
|
-
ParserAdapter,
|
|
13
|
-
ParseResult,
|
|
14
|
-
ASTEntity,
|
|
15
|
-
ASTEntityKind,
|
|
16
|
-
ImportRelation,
|
|
17
|
-
ExportRelation,
|
|
18
|
-
SemanticPatch,
|
|
19
|
-
} from './types.js';
|
|
20
|
-
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
// Parser Adapter
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
export const rubyParser: ParserAdapter = {
|
|
26
|
-
languages: ['ruby'],
|
|
27
|
-
|
|
28
|
-
parse(content: string, filePath: string): ParseResult {
|
|
29
|
-
const fileEntityId = `file:${filePath}`;
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
fileEntityId,
|
|
33
|
-
filePath,
|
|
34
|
-
language: 'ruby',
|
|
35
|
-
declarations: extractDeclarations(content, filePath),
|
|
36
|
-
imports: extractImports(content),
|
|
37
|
-
exports: extractExports(content),
|
|
38
|
-
};
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
diff(oldResult: ParseResult, newResult: ParseResult): SemanticPatch[] {
|
|
42
|
-
return computeSemanticDiff(oldResult, newResult);
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
// Declaration extraction
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
|
|
50
|
-
function extractDeclarations(content: string, filePath: string): ASTEntity[] {
|
|
51
|
-
const declarations: ASTEntity[] = [];
|
|
52
|
-
const lines = content.split('\n');
|
|
53
|
-
const scopeStack: string[] = [];
|
|
54
|
-
|
|
55
|
-
let i = 0;
|
|
56
|
-
while (i < lines.length) {
|
|
57
|
-
const line = lines[i];
|
|
58
|
-
const trimmed = line.trim();
|
|
59
|
-
|
|
60
|
-
// Skip empty lines, comments
|
|
61
|
-
if (!trimmed || trimmed.startsWith('#')) {
|
|
62
|
-
i++;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Class: class Name [< Parent]
|
|
67
|
-
let match = trimmed.match(/^class\s+([A-Z]\w*(?:::\w+)*)/);
|
|
68
|
-
if (match) {
|
|
69
|
-
const name = match[1];
|
|
70
|
-
scopeStack.push(name);
|
|
71
|
-
const result = extractEndBlock(name, 'ClassDef', lines, i, filePath, scopeStack);
|
|
72
|
-
result.entity.children = extractClassMembers(lines, i, result.endLine, name, filePath);
|
|
73
|
-
declarations.push(result.entity);
|
|
74
|
-
scopeStack.pop();
|
|
75
|
-
i = result.endLine + 1;
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Module: module Name
|
|
80
|
-
match = trimmed.match(/^module\s+([A-Z]\w*(?:::\w+)*)/);
|
|
81
|
-
if (match) {
|
|
82
|
-
const name = match[1];
|
|
83
|
-
scopeStack.push(name);
|
|
84
|
-
const result = extractEndBlock(name, 'ClassDef', lines, i, filePath, scopeStack);
|
|
85
|
-
result.entity.children = extractClassMembers(lines, i, result.endLine, name, filePath);
|
|
86
|
-
declarations.push(result.entity);
|
|
87
|
-
scopeStack.pop();
|
|
88
|
-
i = result.endLine + 1;
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Top-level method: def name or def self.name
|
|
93
|
-
match = trimmed.match(/^def\s+(self\.)?(\w+[?!=]?)/);
|
|
94
|
-
if (match) {
|
|
95
|
-
const name = match[1] ? `self.${match[2]}` : match[2];
|
|
96
|
-
const result = extractEndBlock(name, 'FunctionDef', lines, i, filePath, scopeStack);
|
|
97
|
-
declarations.push(result.entity);
|
|
98
|
-
i = result.endLine + 1;
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Constant: NAME = value (top-level, all caps or CamelCase starting with uppercase)
|
|
103
|
-
match = trimmed.match(/^([A-Z][A-Z0-9_]*)\s*=/);
|
|
104
|
-
if (match) {
|
|
105
|
-
declarations.push({
|
|
106
|
-
id: makeEntityId(filePath, 'VariableDecl', match[1]),
|
|
107
|
-
kind: 'VariableDecl',
|
|
108
|
-
name: match[1],
|
|
109
|
-
scopePath: match[1],
|
|
110
|
-
span: [0, 0],
|
|
111
|
-
rawText: trimmed,
|
|
112
|
-
signature: normalizeSignature(trimmed),
|
|
113
|
-
children: [],
|
|
114
|
-
});
|
|
115
|
-
i++;
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
i++;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return declarations;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
// Block extraction
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
|
|
129
|
-
interface ExtractionResult {
|
|
130
|
-
entity: ASTEntity;
|
|
131
|
-
endLine: number;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function extractEndBlock(
|
|
135
|
-
name: string,
|
|
136
|
-
kind: ASTEntityKind,
|
|
137
|
-
lines: string[],
|
|
138
|
-
startLine: number,
|
|
139
|
-
filePath: string,
|
|
140
|
-
scopeStack: string[],
|
|
141
|
-
): ExtractionResult {
|
|
142
|
-
let depth = 0;
|
|
143
|
-
let endLine = startLine;
|
|
144
|
-
|
|
145
|
-
for (let i = startLine; i < lines.length; i++) {
|
|
146
|
-
const trimmed = lines[i].trim();
|
|
147
|
-
|
|
148
|
-
// Count block openers
|
|
149
|
-
if (isBlockOpener(trimmed)) depth++;
|
|
150
|
-
// Count block closers
|
|
151
|
-
if (trimmed === 'end' || trimmed.startsWith('end ') || trimmed.startsWith('end#')) depth--;
|
|
152
|
-
|
|
153
|
-
if (depth <= 0 && i > startLine) { endLine = i; break; }
|
|
154
|
-
endLine = i;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const rawText = lines.slice(startLine, endLine + 1).join('\n');
|
|
158
|
-
const startOffset = lines.slice(0, startLine).join('\n').length + (startLine > 0 ? 1 : 0);
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
entity: {
|
|
162
|
-
id: makeEntityId(filePath, kind, name),
|
|
163
|
-
kind,
|
|
164
|
-
name,
|
|
165
|
-
scopePath: name,
|
|
166
|
-
span: [startOffset, startOffset + rawText.length],
|
|
167
|
-
rawText,
|
|
168
|
-
signature: normalizeSignature(rawText),
|
|
169
|
-
children: [],
|
|
170
|
-
},
|
|
171
|
-
endLine,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function isBlockOpener(trimmed: string): boolean {
|
|
176
|
-
// Match: class, module, def, do, if/unless/while/until/for/case/begin (as statements, not modifiers)
|
|
177
|
-
if (/^(class|module|def|do|begin|case)\b/.test(trimmed)) return true;
|
|
178
|
-
if (/^(if|unless|while|until|for)\b/.test(trimmed) && !trimmed.includes(' then ')) return true;
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ---------------------------------------------------------------------------
|
|
183
|
-
// Class member extraction
|
|
184
|
-
// ---------------------------------------------------------------------------
|
|
185
|
-
|
|
186
|
-
function extractClassMembers(
|
|
187
|
-
lines: string[],
|
|
188
|
-
startLine: number,
|
|
189
|
-
endLine: number,
|
|
190
|
-
parentName: string,
|
|
191
|
-
filePath: string,
|
|
192
|
-
): ASTEntity[] {
|
|
193
|
-
const children: ASTEntity[] = [];
|
|
194
|
-
let depth = 0;
|
|
195
|
-
|
|
196
|
-
for (let i = startLine; i <= endLine; i++) {
|
|
197
|
-
const trimmed = lines[i].trim();
|
|
198
|
-
const depthBefore = depth;
|
|
199
|
-
|
|
200
|
-
if (isBlockOpener(trimmed)) depth++;
|
|
201
|
-
if (trimmed === 'end' || trimmed.startsWith('end ') || trimmed.startsWith('end#')) depth--;
|
|
202
|
-
|
|
203
|
-
// Only look at depth 1 (direct children)
|
|
204
|
-
if (depthBefore === 1) {
|
|
205
|
-
// Method: def name or def self.name
|
|
206
|
-
const match = trimmed.match(/^def\s+(self\.)?(\w+[?!=]?)/);
|
|
207
|
-
if (match) {
|
|
208
|
-
const methodName = match[1] ? `self.${match[2]}` : match[2];
|
|
209
|
-
const kind: ASTEntityKind = methodName === 'initialize' ? 'Constructor' : 'MethodDef';
|
|
210
|
-
children.push({
|
|
211
|
-
id: makeEntityId(filePath, kind, `${parentName}.${methodName}`),
|
|
212
|
-
kind,
|
|
213
|
-
name: methodName,
|
|
214
|
-
scopePath: `${parentName}.${methodName}`,
|
|
215
|
-
span: [0, 0],
|
|
216
|
-
rawText: trimmed,
|
|
217
|
-
signature: normalizeSignature(trimmed),
|
|
218
|
-
children: [],
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// attr_accessor / attr_reader / attr_writer
|
|
223
|
-
const attrMatch = trimmed.match(/^attr_(accessor|reader|writer)\s+(.*)/);
|
|
224
|
-
if (attrMatch) {
|
|
225
|
-
const attrs = attrMatch[2].split(',').map(a => a.trim().replace(/^:/, ''));
|
|
226
|
-
for (const attr of attrs) {
|
|
227
|
-
if (attr) {
|
|
228
|
-
children.push({
|
|
229
|
-
id: makeEntityId(filePath, 'PropertyDef', `${parentName}.${attr}`),
|
|
230
|
-
kind: 'PropertyDef',
|
|
231
|
-
name: attr,
|
|
232
|
-
scopePath: `${parentName}.${attr}`,
|
|
233
|
-
span: [0, 0],
|
|
234
|
-
rawText: trimmed,
|
|
235
|
-
signature: normalizeSignature(trimmed),
|
|
236
|
-
children: [],
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return children;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// ---------------------------------------------------------------------------
|
|
247
|
-
// Import extraction
|
|
248
|
-
// ---------------------------------------------------------------------------
|
|
249
|
-
|
|
250
|
-
function extractImports(content: string): ImportRelation[] {
|
|
251
|
-
const imports: ImportRelation[] = [];
|
|
252
|
-
const lines = content.split('\n');
|
|
253
|
-
|
|
254
|
-
for (const line of lines) {
|
|
255
|
-
const trimmed = line.trim();
|
|
256
|
-
|
|
257
|
-
// require 'name' or require "name"
|
|
258
|
-
let match = trimmed.match(/^require\s+['"]([^'"]+)['"]/);
|
|
259
|
-
if (match) {
|
|
260
|
-
imports.push({
|
|
261
|
-
source: match[1],
|
|
262
|
-
specifiers: [match[1].split('/').pop()!],
|
|
263
|
-
isDefault: true,
|
|
264
|
-
isNamespace: false,
|
|
265
|
-
rawText: trimmed,
|
|
266
|
-
span: [0, trimmed.length],
|
|
267
|
-
});
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// require_relative 'name'
|
|
272
|
-
match = trimmed.match(/^require_relative\s+['"]([^'"]+)['"]/);
|
|
273
|
-
if (match) {
|
|
274
|
-
imports.push({
|
|
275
|
-
source: match[1],
|
|
276
|
-
specifiers: [match[1].split('/').pop()!],
|
|
277
|
-
isDefault: true,
|
|
278
|
-
isNamespace: false,
|
|
279
|
-
rawText: trimmed,
|
|
280
|
-
span: [0, trimmed.length],
|
|
281
|
-
});
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// include ModuleName / extend ModuleName / prepend ModuleName
|
|
286
|
-
match = trimmed.match(/^(include|extend|prepend)\s+([A-Z]\w*(?:::\w+)*)/);
|
|
287
|
-
if (match) {
|
|
288
|
-
imports.push({
|
|
289
|
-
source: match[2],
|
|
290
|
-
specifiers: [match[2]],
|
|
291
|
-
isDefault: false,
|
|
292
|
-
isNamespace: true,
|
|
293
|
-
rawText: trimmed,
|
|
294
|
-
span: [0, trimmed.length],
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return imports;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// ---------------------------------------------------------------------------
|
|
303
|
-
// Export extraction
|
|
304
|
-
// ---------------------------------------------------------------------------
|
|
305
|
-
|
|
306
|
-
function extractExports(content: string): ExportRelation[] {
|
|
307
|
-
const exports: ExportRelation[] = [];
|
|
308
|
-
const lines = content.split('\n');
|
|
309
|
-
|
|
310
|
-
for (const line of lines) {
|
|
311
|
-
const trimmed = line.trim();
|
|
312
|
-
|
|
313
|
-
// module_function :name
|
|
314
|
-
const match = trimmed.match(/^module_function\s+:(\w+)/);
|
|
315
|
-
if (match) {
|
|
316
|
-
exports.push({
|
|
317
|
-
name: match[1],
|
|
318
|
-
isDefault: false,
|
|
319
|
-
rawText: trimmed,
|
|
320
|
-
span: [0, 0],
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// public :name (explicit public declaration)
|
|
325
|
-
const pubMatch = trimmed.match(/^public\s+:(\w+)/);
|
|
326
|
-
if (pubMatch) {
|
|
327
|
-
exports.push({
|
|
328
|
-
name: pubMatch[1],
|
|
329
|
-
isDefault: false,
|
|
330
|
-
rawText: trimmed,
|
|
331
|
-
span: [0, 0],
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return exports;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// ---------------------------------------------------------------------------
|
|
340
|
-
// Semantic diff
|
|
341
|
-
// ---------------------------------------------------------------------------
|
|
342
|
-
|
|
343
|
-
function computeSemanticDiff(
|
|
344
|
-
oldResult: ParseResult,
|
|
345
|
-
newResult: ParseResult,
|
|
346
|
-
): SemanticPatch[] {
|
|
347
|
-
const patches: SemanticPatch[] = [];
|
|
348
|
-
const fileId = newResult.fileEntityId;
|
|
349
|
-
|
|
350
|
-
const oldDecls = new Map(oldResult.declarations.map(d => [d.id, d]));
|
|
351
|
-
const newDecls = new Map(newResult.declarations.map(d => [d.id, d]));
|
|
352
|
-
|
|
353
|
-
for (const [id, entity] of newDecls) {
|
|
354
|
-
if (!oldDecls.has(id)) {
|
|
355
|
-
const oldEntity = findRenamedEntity(entity, oldResult.declarations, newDecls);
|
|
356
|
-
if (oldEntity) {
|
|
357
|
-
patches.push({ kind: 'symbolRename', entityId: oldEntity.id, oldName: oldEntity.name, newName: entity.name });
|
|
358
|
-
} else {
|
|
359
|
-
patches.push({ kind: 'symbolAdd', entity });
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
for (const [id, entity] of oldDecls) {
|
|
365
|
-
if (!newDecls.has(id)) {
|
|
366
|
-
const wasRenamed = findRenamedEntity(entity, newResult.declarations, oldDecls);
|
|
367
|
-
if (!wasRenamed) {
|
|
368
|
-
patches.push({ kind: 'symbolRemove', entityId: id, entityName: entity.name });
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
for (const [id, newEntity] of newDecls) {
|
|
374
|
-
const oldEntity = oldDecls.get(id);
|
|
375
|
-
if (oldEntity && oldEntity.signature !== newEntity.signature) {
|
|
376
|
-
patches.push({
|
|
377
|
-
kind: 'symbolModify', entityId: id, entityName: newEntity.name,
|
|
378
|
-
oldSignature: oldEntity.signature, newSignature: newEntity.signature,
|
|
379
|
-
oldRawText: oldEntity.rawText, newRawText: newEntity.rawText,
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const oldImports = new Map(oldResult.imports.map(imp => [imp.source, imp]));
|
|
385
|
-
const newImports = new Map(newResult.imports.map(imp => [imp.source, imp]));
|
|
386
|
-
|
|
387
|
-
for (const [source, imp] of newImports) {
|
|
388
|
-
const oldImp = oldImports.get(source);
|
|
389
|
-
if (!oldImp) {
|
|
390
|
-
patches.push({ kind: 'importAdd', fileId, source, specifiers: imp.specifiers, rawText: imp.rawText });
|
|
391
|
-
} else if (JSON.stringify(oldImp.specifiers.sort()) !== JSON.stringify(imp.specifiers.sort())) {
|
|
392
|
-
patches.push({ kind: 'importModify', fileId, source, oldSpecifiers: oldImp.specifiers, newSpecifiers: imp.specifiers });
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
for (const [source] of oldImports) {
|
|
396
|
-
if (!newImports.has(source)) {
|
|
397
|
-
patches.push({ kind: 'importRemove', fileId, source });
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const oldExports = new Map(oldResult.exports.map(exp => [exp.name, exp]));
|
|
402
|
-
const newExports = new Map(newResult.exports.map(exp => [exp.name, exp]));
|
|
403
|
-
for (const [name, exp] of newExports) {
|
|
404
|
-
if (!oldExports.has(name)) {
|
|
405
|
-
patches.push({ kind: 'exportAdd', fileId, name, rawText: exp.rawText });
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
for (const [name] of oldExports) {
|
|
409
|
-
if (!newExports.has(name)) {
|
|
410
|
-
patches.push({ kind: 'exportRemove', fileId, name });
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
return patches;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// ---------------------------------------------------------------------------
|
|
418
|
-
// Helpers
|
|
419
|
-
// ---------------------------------------------------------------------------
|
|
420
|
-
|
|
421
|
-
function findRenamedEntity(
|
|
422
|
-
entity: ASTEntity,
|
|
423
|
-
candidates: ASTEntity[],
|
|
424
|
-
existingIds: Map<string, ASTEntity>,
|
|
425
|
-
): ASTEntity | null {
|
|
426
|
-
for (const candidate of candidates) {
|
|
427
|
-
if (candidate.kind !== entity.kind) continue;
|
|
428
|
-
if (candidate.name === entity.name) continue;
|
|
429
|
-
if (existingIds.has(candidate.id)) continue;
|
|
430
|
-
const normalizedOld = candidate.signature.replace(new RegExp(candidate.name, 'g'), '___');
|
|
431
|
-
const normalizedNew = entity.signature.replace(new RegExp(entity.name, 'g'), '___');
|
|
432
|
-
if (normalizedOld === normalizedNew) return candidate;
|
|
433
|
-
}
|
|
434
|
-
return null;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
function makeEntityId(filePath: string, kind: string, name: string): string {
|
|
438
|
-
return `${kind}:${filePath}:${name}`;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function normalizeSignature(text: string): string {
|
|
442
|
-
return text
|
|
443
|
-
.replace(/#[^\n]*/g, '')
|
|
444
|
-
.replace(/\s+/g, ' ')
|
|
445
|
-
.trim();
|
|
446
|
-
}
|