@workos/oagen 0.1.0 → 0.2.0
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.d.mts +2 -0
- package/dist/cli/index.mjs +6018 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/compat/{index.d.ts → index.d.mts} +43 -114
- package/dist/compat/index.d.mts.map +1 -0
- package/dist/compat/index.mjs +4562 -0
- package/dist/compat/index.mjs.map +1 -0
- package/dist/diff-DyQF4sel.mjs +2250 -0
- package/dist/diff-DyQF4sel.mjs.map +1 -0
- package/dist/differ-BwelBJN0.mjs +1100 -0
- package/dist/differ-BwelBJN0.mjs.map +1 -0
- package/dist/index.d.mts +295 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1553 -0
- package/dist/index.mjs.map +1 -0
- package/dist/scripts/smoke/baseline.d.mts +1 -0
- package/dist/scripts/smoke/baseline.mjs +1916 -0
- package/dist/scripts/smoke/baseline.mjs.map +1 -0
- package/dist/scripts/smoke/sdk-test.d.mts +1 -0
- package/dist/scripts/smoke/sdk-test.mjs +111 -0
- package/dist/scripts/smoke/sdk-test.mjs.map +1 -0
- package/dist/smoke.d.mts +331 -0
- package/dist/smoke.d.mts.map +1 -0
- package/dist/smoke.mjs +2084 -0
- package/dist/smoke.mjs.map +1 -0
- package/dist/tree-sitter-BPNfbEzY.mjs +359 -0
- package/dist/tree-sitter-BPNfbEzY.mjs.map +1 -0
- package/dist/types-O25qZxMl.d.mts +397 -0
- package/dist/types-O25qZxMl.d.mts.map +1 -0
- package/dist/verify/index.d.mts +82 -0
- package/dist/verify/index.d.mts.map +1 -0
- package/dist/verify/index.mjs +327 -0
- package/dist/verify/index.mjs.map +1 -0
- package/package.json +19 -16
- package/dist/chunk-CIX5NOBF.js +0 -263
- package/dist/chunk-IJ5E5AE7.js +0 -1040
- package/dist/chunk-VUSRNIG2.js +0 -1661
- package/dist/cli/index.js +0 -5314
- package/dist/compat/index.js +0 -4491
- package/dist/index.d.ts +0 -277
- package/dist/index.js +0 -1565
- package/dist/scripts/smoke/baseline.js +0 -1813
- package/dist/scripts/smoke/sdk-test.js +0 -93
- package/dist/smoke.js +0 -2025
- package/dist/types-B5t9O9Wm.d.ts +0 -367
- package/dist/verify/index.d.ts +0 -76
- package/dist/verify/index.js +0 -336
package/dist/chunk-VUSRNIG2.js
DELETED
|
@@ -1,1661 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RegistryError,
|
|
3
|
-
assertNever,
|
|
4
|
-
normalizeJsExtension,
|
|
5
|
-
safeParse,
|
|
6
|
-
toSnakeCase
|
|
7
|
-
} from "./chunk-CIX5NOBF.js";
|
|
8
|
-
|
|
9
|
-
// src/engine/generate-files.ts
|
|
10
|
-
function buildEmitterContext(spec, options) {
|
|
11
|
-
return {
|
|
12
|
-
namespace: toSnakeCase(options.namespace),
|
|
13
|
-
namespacePascal: options.namespace,
|
|
14
|
-
spec,
|
|
15
|
-
outputDir: options.outputDir,
|
|
16
|
-
apiSurface: options.apiSurface,
|
|
17
|
-
overlayLookup: options.overlayLookup
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
function generateAllFiles(spec, emitter, ctx) {
|
|
21
|
-
return [
|
|
22
|
-
...emitter.generateModels(spec.models, ctx),
|
|
23
|
-
...emitter.generateEnums(spec.enums, ctx),
|
|
24
|
-
...emitter.generateResources(spec.services, ctx),
|
|
25
|
-
...emitter.generateClient(spec, ctx),
|
|
26
|
-
...emitter.generateErrors(ctx),
|
|
27
|
-
...emitter.generateConfig(ctx),
|
|
28
|
-
...emitter.generateTypeSignatures?.(spec, ctx) ?? [],
|
|
29
|
-
...emitter.generateTests(spec, ctx),
|
|
30
|
-
...emitter.generateManifest?.(spec, ctx) ?? []
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
function applyFileHeaders(files, header) {
|
|
34
|
-
return files.map((f) => ({
|
|
35
|
-
...f,
|
|
36
|
-
content: !header || f.path.endsWith(".json") || f.headerPlacement === "skip" ? f.content : header + "\n\n" + f.content,
|
|
37
|
-
skipIfExists: f.skipIfExists ?? false
|
|
38
|
-
}));
|
|
39
|
-
}
|
|
40
|
-
function generateFiles(spec, emitter, options) {
|
|
41
|
-
const ctx = buildEmitterContext(spec, options);
|
|
42
|
-
const files = generateAllFiles(spec, emitter, ctx);
|
|
43
|
-
const header = emitter.fileHeader();
|
|
44
|
-
return { files: applyFileHeaders(files, header), ctx, header };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// src/engine/writer.ts
|
|
48
|
-
import * as fs from "fs/promises";
|
|
49
|
-
import * as path from "path";
|
|
50
|
-
|
|
51
|
-
// src/engine/merger.ts
|
|
52
|
-
import Parser from "tree-sitter";
|
|
53
|
-
|
|
54
|
-
// src/engine/merge-adapters/go.ts
|
|
55
|
-
function lastIdentifier(text) {
|
|
56
|
-
const matches = [...text.matchAll(/[A-Za-z_][A-Za-z0-9_]*/g)];
|
|
57
|
-
return matches.length > 0 ? matches[matches.length - 1][0] : null;
|
|
58
|
-
}
|
|
59
|
-
function receiverKey(node) {
|
|
60
|
-
const receiver = node.childForFieldName("receiver");
|
|
61
|
-
if (!receiver) return null;
|
|
62
|
-
return lastIdentifier(receiver.text);
|
|
63
|
-
}
|
|
64
|
-
function declarationKey(node) {
|
|
65
|
-
switch (node.type) {
|
|
66
|
-
case "package_clause": {
|
|
67
|
-
const pkg = lastIdentifier(node.text);
|
|
68
|
-
return pkg ? `__package:${pkg}` : "__package:";
|
|
69
|
-
}
|
|
70
|
-
case "type_declaration": {
|
|
71
|
-
const spec = node.firstNamedChild?.type === "type_spec" ? node.firstNamedChild : node.namedChildren[0];
|
|
72
|
-
const nameNode = spec?.childForFieldName("name");
|
|
73
|
-
return nameNode?.text ?? null;
|
|
74
|
-
}
|
|
75
|
-
case "function_declaration": {
|
|
76
|
-
return node.childForFieldName("name")?.text ?? null;
|
|
77
|
-
}
|
|
78
|
-
case "method_declaration": {
|
|
79
|
-
const name = node.childForFieldName("name")?.text ?? null;
|
|
80
|
-
const receiver = receiverKey(node);
|
|
81
|
-
return name && receiver ? `method:${receiver}.${name}` : name;
|
|
82
|
-
}
|
|
83
|
-
case "const_declaration":
|
|
84
|
-
case "var_declaration": {
|
|
85
|
-
return node.text.split(/\s+/).slice(0, 3).join(" ");
|
|
86
|
-
}
|
|
87
|
-
default:
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function importEntries(node, source) {
|
|
92
|
-
const entries = [];
|
|
93
|
-
const collect = (spec) => {
|
|
94
|
-
const path2 = spec.childForFieldName("path")?.text ?? spec.text;
|
|
95
|
-
const name = spec.childForFieldName("name")?.text ?? "";
|
|
96
|
-
const text = source.slice(spec.startIndex, spec.endIndex);
|
|
97
|
-
entries.push({
|
|
98
|
-
key: `${name}:${path2.replace(/^"|"$/g, "")}`,
|
|
99
|
-
text
|
|
100
|
-
});
|
|
101
|
-
};
|
|
102
|
-
for (const child of node.namedChildren) {
|
|
103
|
-
if (child.type === "import_spec") {
|
|
104
|
-
collect(child);
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (child.type === "import_spec_list") {
|
|
108
|
-
for (const spec of child.namedChildren) {
|
|
109
|
-
if (spec.type === "import_spec") collect(spec);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return entries;
|
|
114
|
-
}
|
|
115
|
-
function collectPrecedingGoComments(children, index, source) {
|
|
116
|
-
let lastIdx = -1;
|
|
117
|
-
let firstIdx = -1;
|
|
118
|
-
for (let k = index - 1; k >= 0; k--) {
|
|
119
|
-
const prev = children[k];
|
|
120
|
-
if (prev.type === "comment") {
|
|
121
|
-
const text = source.slice(prev.startIndex, prev.endIndex);
|
|
122
|
-
if (text.startsWith("//")) {
|
|
123
|
-
if (lastIdx === -1) lastIdx = k;
|
|
124
|
-
firstIdx = k;
|
|
125
|
-
} else {
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (firstIdx === -1) return null;
|
|
133
|
-
const first = children[firstIdx];
|
|
134
|
-
const last = children[lastIdx];
|
|
135
|
-
return { text: source.slice(first.startIndex, last.endIndex), startIndex: first.startIndex, endIndex: last.endIndex };
|
|
136
|
-
}
|
|
137
|
-
var goMergeAdapter = {
|
|
138
|
-
language: "go",
|
|
139
|
-
grammarModule: "tree-sitter-go",
|
|
140
|
-
parseStatements(tree, source) {
|
|
141
|
-
const imports = [];
|
|
142
|
-
const importAnchors = [];
|
|
143
|
-
let importInsertionAnchor;
|
|
144
|
-
const statements = [];
|
|
145
|
-
for (const child of tree.rootNode.children) {
|
|
146
|
-
if (child.type === "comment") continue;
|
|
147
|
-
if (child.type === "package_clause") {
|
|
148
|
-
importInsertionAnchor = source.slice(child.startIndex, child.endIndex);
|
|
149
|
-
}
|
|
150
|
-
if (child.type === "import_declaration") {
|
|
151
|
-
imports.push(...importEntries(child, source));
|
|
152
|
-
importAnchors.push(source.slice(child.startIndex, child.endIndex));
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
const key = declarationKey(child);
|
|
156
|
-
statements.push({
|
|
157
|
-
key,
|
|
158
|
-
kind: key ? "declaration" : "other",
|
|
159
|
-
text: source.slice(child.startIndex, child.endIndex)
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
return { imports, importAnchors, importInsertionAnchor, statements };
|
|
163
|
-
},
|
|
164
|
-
renderImports(imports) {
|
|
165
|
-
if (imports.length === 0) return [];
|
|
166
|
-
if (imports.length === 1) return [`import ${imports[0].text}`];
|
|
167
|
-
return [`import (
|
|
168
|
-
${imports.map((entry) => ` ${entry.text}`).join("\n")}
|
|
169
|
-
)`];
|
|
170
|
-
},
|
|
171
|
-
extractDocstrings(tree, source) {
|
|
172
|
-
const result = /* @__PURE__ */ new Map();
|
|
173
|
-
const rootChildren = tree.rootNode.children;
|
|
174
|
-
for (let i = 0; i < rootChildren.length; i++) {
|
|
175
|
-
const child = rootChildren[i];
|
|
176
|
-
const key = declarationKey(child);
|
|
177
|
-
if (!key || key.startsWith("__package:")) continue;
|
|
178
|
-
const docstring = collectPrecedingGoComments(rootChildren, i, source);
|
|
179
|
-
result.set(key, {
|
|
180
|
-
docstring,
|
|
181
|
-
declStartIndex: child.startIndex,
|
|
182
|
-
declColumn: child.startPosition.column,
|
|
183
|
-
members: /* @__PURE__ */ new Map()
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
return result;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
// src/engine/merge-adapters/node.ts
|
|
191
|
-
var REEXPORT_PREFIX = "__export:";
|
|
192
|
-
function extractDeclName(node) {
|
|
193
|
-
const nameNode = node.childForFieldName("name");
|
|
194
|
-
if (nameNode) return nameNode.text;
|
|
195
|
-
if (node.type === "lexical_declaration") {
|
|
196
|
-
const declarator = node.firstNamedChild;
|
|
197
|
-
if (declarator?.type === "variable_declarator") {
|
|
198
|
-
const name = declarator.childForFieldName("name");
|
|
199
|
-
if (name) return name.text;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
function extractNodeKey(node) {
|
|
205
|
-
if (node.type === "export_statement") {
|
|
206
|
-
const decl = node.childForFieldName("declaration");
|
|
207
|
-
if (decl) {
|
|
208
|
-
return { key: extractDeclName(decl), kind: "declaration" };
|
|
209
|
-
}
|
|
210
|
-
const source = node.childForFieldName("source");
|
|
211
|
-
if (source) {
|
|
212
|
-
return { key: `${REEXPORT_PREFIX}${normalizeJsExtension(source.text)}`, kind: "reexport" };
|
|
213
|
-
}
|
|
214
|
-
return { key: null, kind: "other" };
|
|
215
|
-
}
|
|
216
|
-
return { key: extractDeclName(node), kind: extractDeclName(node) ? "declaration" : "other" };
|
|
217
|
-
}
|
|
218
|
-
function extractClassMembers(classBody, source) {
|
|
219
|
-
const members = [];
|
|
220
|
-
for (const child of classBody.namedChildren) {
|
|
221
|
-
if (child.type === "method_definition") {
|
|
222
|
-
const nameNode = child.childForFieldName("name");
|
|
223
|
-
if (nameNode && nameNode.text !== "constructor") {
|
|
224
|
-
members.push({ key: nameNode.text, text: source.slice(child.startIndex, child.endIndex) });
|
|
225
|
-
}
|
|
226
|
-
} else if (child.type === "public_field_definition") {
|
|
227
|
-
const nameNode = child.childForFieldName("name");
|
|
228
|
-
if (nameNode) {
|
|
229
|
-
members.push({ key: nameNode.text, text: source.slice(child.startIndex, child.endIndex) });
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return members;
|
|
234
|
-
}
|
|
235
|
-
function extractInterfaceMembers(body, source) {
|
|
236
|
-
const members = [];
|
|
237
|
-
for (const child of body.namedChildren) {
|
|
238
|
-
if (child.type === "property_signature" || child.type === "method_signature") {
|
|
239
|
-
const nameNode = child.childForFieldName("name");
|
|
240
|
-
if (nameNode) {
|
|
241
|
-
members.push({ key: nameNode.text, text: source.slice(child.startIndex, child.endIndex) });
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return members;
|
|
246
|
-
}
|
|
247
|
-
function extractEnumMembers(body, source) {
|
|
248
|
-
const members = [];
|
|
249
|
-
for (const child of body.namedChildren) {
|
|
250
|
-
if (child.type === "enum_assignment") {
|
|
251
|
-
const nameNode = child.childForFieldName("name");
|
|
252
|
-
if (nameNode) {
|
|
253
|
-
members.push({ key: nameNode.text, text: source.slice(child.startIndex, child.endIndex) });
|
|
254
|
-
}
|
|
255
|
-
} else if (child.type === "property_identifier") {
|
|
256
|
-
members.push({ key: child.text, text: source.slice(child.startIndex, child.endIndex) });
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return members;
|
|
260
|
-
}
|
|
261
|
-
function isCommentNode(node) {
|
|
262
|
-
return node.type === "comment" || node.grammarType === "comment";
|
|
263
|
-
}
|
|
264
|
-
function findPrecedingDocstring(children, index, source) {
|
|
265
|
-
for (let k = index - 1; k >= 0; k--) {
|
|
266
|
-
const prev = children[k];
|
|
267
|
-
if (isCommentNode(prev)) {
|
|
268
|
-
const text = source.slice(prev.startIndex, prev.endIndex);
|
|
269
|
-
if (text.startsWith("/**")) {
|
|
270
|
-
return { text, startIndex: prev.startIndex, endIndex: prev.endIndex };
|
|
271
|
-
}
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
if (prev.type === "," || prev.type === ";" || prev.type === "{") continue;
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
var CLASS_MEMBER_TYPES = /* @__PURE__ */ new Set(["method_definition", "public_field_definition"]);
|
|
280
|
-
var INTERFACE_MEMBER_TYPES = /* @__PURE__ */ new Set(["property_signature", "method_signature"]);
|
|
281
|
-
var ENUM_MEMBER_TYPES = /* @__PURE__ */ new Set(["enum_assignment", "property_identifier"]);
|
|
282
|
-
function extractBodyMemberDocstrings(body, source, memberTypes) {
|
|
283
|
-
const result = /* @__PURE__ */ new Map();
|
|
284
|
-
const allChildren = body.children;
|
|
285
|
-
for (let i = 0; i < allChildren.length; i++) {
|
|
286
|
-
const child = allChildren[i];
|
|
287
|
-
if (!memberTypes.has(child.type)) continue;
|
|
288
|
-
let memberName = null;
|
|
289
|
-
if (child.type === "property_identifier") {
|
|
290
|
-
memberName = child.text;
|
|
291
|
-
} else {
|
|
292
|
-
const nameNode = child.childForFieldName("name");
|
|
293
|
-
if (nameNode) memberName = nameNode.text;
|
|
294
|
-
}
|
|
295
|
-
if (!memberName || memberName === "constructor") continue;
|
|
296
|
-
const docstring = findPrecedingDocstring(allChildren, i, source);
|
|
297
|
-
result.set(memberName, {
|
|
298
|
-
docstring,
|
|
299
|
-
declStartIndex: child.startIndex,
|
|
300
|
-
declColumn: child.startPosition.column
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
return result;
|
|
304
|
-
}
|
|
305
|
-
var nodeMergeAdapter = {
|
|
306
|
-
language: "node",
|
|
307
|
-
grammarModule: "tree-sitter-typescript/bindings/node/typescript.js",
|
|
308
|
-
normalizeReexport: normalizeJsExtension,
|
|
309
|
-
parseStatements(tree, source) {
|
|
310
|
-
const imports = [];
|
|
311
|
-
const importAnchors = [];
|
|
312
|
-
const statements = [];
|
|
313
|
-
for (const child of tree.rootNode.children) {
|
|
314
|
-
if (child.type === "comment") continue;
|
|
315
|
-
if (child.type === "import_statement") {
|
|
316
|
-
const text = source.slice(child.startIndex, child.endIndex);
|
|
317
|
-
const sourceNode = child.childForFieldName("source");
|
|
318
|
-
const modulePath = sourceNode ? normalizeJsExtension(sourceNode.text) : normalizeJsExtension(text.trim());
|
|
319
|
-
imports.push({ key: modulePath, text });
|
|
320
|
-
importAnchors.push(text);
|
|
321
|
-
continue;
|
|
322
|
-
}
|
|
323
|
-
const { key, kind } = extractNodeKey(child);
|
|
324
|
-
statements.push({
|
|
325
|
-
key,
|
|
326
|
-
kind,
|
|
327
|
-
text: source.slice(child.startIndex, child.endIndex)
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
return { imports, importAnchors, statements };
|
|
331
|
-
},
|
|
332
|
-
renderImports(imports) {
|
|
333
|
-
return imports.map((entry) => entry.text);
|
|
334
|
-
},
|
|
335
|
-
extractMembers(tree, source) {
|
|
336
|
-
const result = /* @__PURE__ */ new Map();
|
|
337
|
-
for (const child of tree.rootNode.children) {
|
|
338
|
-
if (child.type !== "export_statement") continue;
|
|
339
|
-
const decl = child.childForFieldName("declaration");
|
|
340
|
-
if (!decl) continue;
|
|
341
|
-
const nameNode = decl.childForFieldName("name");
|
|
342
|
-
if (!nameNode) continue;
|
|
343
|
-
const symbolName = nameNode.text;
|
|
344
|
-
if (decl.type === "class_declaration") {
|
|
345
|
-
const body = decl.childForFieldName("body");
|
|
346
|
-
if (!body) continue;
|
|
347
|
-
const members = extractClassMembers(body, source);
|
|
348
|
-
result.set(symbolName, { members, bodyEndLine: body.endPosition.row });
|
|
349
|
-
} else if (decl.type === "interface_declaration") {
|
|
350
|
-
const body = decl.childForFieldName("body");
|
|
351
|
-
if (!body) continue;
|
|
352
|
-
const members = extractInterfaceMembers(body, source);
|
|
353
|
-
result.set(symbolName, { members, bodyEndLine: body.endPosition.row });
|
|
354
|
-
} else if (decl.type === "enum_declaration") {
|
|
355
|
-
const body = decl.childForFieldName("body");
|
|
356
|
-
if (!body) continue;
|
|
357
|
-
const members = extractEnumMembers(body, source);
|
|
358
|
-
result.set(symbolName, { members, bodyEndLine: body.endPosition.row });
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return result;
|
|
362
|
-
},
|
|
363
|
-
extractDocstrings(tree, source) {
|
|
364
|
-
const result = /* @__PURE__ */ new Map();
|
|
365
|
-
const rootChildren = tree.rootNode.children;
|
|
366
|
-
for (let i = 0; i < rootChildren.length; i++) {
|
|
367
|
-
const child = rootChildren[i];
|
|
368
|
-
if (child.type !== "export_statement") continue;
|
|
369
|
-
const decl = child.childForFieldName("declaration");
|
|
370
|
-
if (!decl) continue;
|
|
371
|
-
const nameNode = decl.childForFieldName("name");
|
|
372
|
-
if (!nameNode) continue;
|
|
373
|
-
const docstring = findPrecedingDocstring(rootChildren, i, source);
|
|
374
|
-
let members = /* @__PURE__ */ new Map();
|
|
375
|
-
const body = decl.childForFieldName("body");
|
|
376
|
-
if (body) {
|
|
377
|
-
if (decl.type === "class_declaration") {
|
|
378
|
-
members = extractBodyMemberDocstrings(body, source, CLASS_MEMBER_TYPES);
|
|
379
|
-
} else if (decl.type === "interface_declaration") {
|
|
380
|
-
members = extractBodyMemberDocstrings(body, source, INTERFACE_MEMBER_TYPES);
|
|
381
|
-
} else if (decl.type === "enum_declaration") {
|
|
382
|
-
members = extractBodyMemberDocstrings(body, source, ENUM_MEMBER_TYPES);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
result.set(nameNode.text, {
|
|
386
|
-
docstring,
|
|
387
|
-
declStartIndex: child.startIndex,
|
|
388
|
-
declColumn: child.startPosition.column,
|
|
389
|
-
members
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
return result;
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// src/engine/merge-adapters/php.ts
|
|
397
|
-
function extractPhpDeclarationName(node) {
|
|
398
|
-
switch (node.type) {
|
|
399
|
-
case "class_declaration":
|
|
400
|
-
case "interface_declaration":
|
|
401
|
-
case "trait_declaration":
|
|
402
|
-
case "enum_declaration":
|
|
403
|
-
case "function_definition": {
|
|
404
|
-
const nameNode = node.childForFieldName("name");
|
|
405
|
-
return nameNode?.text ?? null;
|
|
406
|
-
}
|
|
407
|
-
case "namespace_definition": {
|
|
408
|
-
const nameNode = node.childForFieldName("name");
|
|
409
|
-
return nameNode ? `__namespace:${nameNode.text}` : "__namespace:";
|
|
410
|
-
}
|
|
411
|
-
default:
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
function findPhpDocstring(children, index, source) {
|
|
416
|
-
for (let k = index - 1; k >= 0; k--) {
|
|
417
|
-
const prev = children[k];
|
|
418
|
-
if (prev.type === "comment") {
|
|
419
|
-
const text = source.slice(prev.startIndex, prev.endIndex);
|
|
420
|
-
if (text.startsWith("/**")) {
|
|
421
|
-
return { text, startIndex: prev.startIndex, endIndex: prev.endIndex };
|
|
422
|
-
}
|
|
423
|
-
return null;
|
|
424
|
-
}
|
|
425
|
-
if (prev.type === ";" || prev.type === "{") continue;
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
return null;
|
|
429
|
-
}
|
|
430
|
-
var phpMergeAdapter = {
|
|
431
|
-
language: "php",
|
|
432
|
-
grammarModule: "tree-sitter-php",
|
|
433
|
-
resolveGrammar: (mod) => {
|
|
434
|
-
if (typeof mod === "object" && mod !== null && "php" in mod) return mod.php;
|
|
435
|
-
return mod;
|
|
436
|
-
},
|
|
437
|
-
parseStatements(tree, source) {
|
|
438
|
-
const imports = [];
|
|
439
|
-
const importAnchors = [];
|
|
440
|
-
let importInsertionAnchor;
|
|
441
|
-
const statements = [];
|
|
442
|
-
for (const child of tree.rootNode.children) {
|
|
443
|
-
if (child.type === "comment") continue;
|
|
444
|
-
if (child.type === "namespace_definition") {
|
|
445
|
-
importInsertionAnchor = source.slice(child.startIndex, child.endIndex);
|
|
446
|
-
}
|
|
447
|
-
if (child.type === "namespace_use_declaration") {
|
|
448
|
-
const text = source.slice(child.startIndex, child.endIndex);
|
|
449
|
-
imports.push({ key: text.trim(), text });
|
|
450
|
-
importAnchors.push(text);
|
|
451
|
-
continue;
|
|
452
|
-
}
|
|
453
|
-
const declarationName = extractPhpDeclarationName(child);
|
|
454
|
-
const kind = declarationName ? "declaration" : "other";
|
|
455
|
-
statements.push({
|
|
456
|
-
key: declarationName,
|
|
457
|
-
kind,
|
|
458
|
-
text: source.slice(child.startIndex, child.endIndex)
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
return { imports, importAnchors, importInsertionAnchor, statements };
|
|
462
|
-
},
|
|
463
|
-
renderImports(imports) {
|
|
464
|
-
return imports.map((entry) => entry.text);
|
|
465
|
-
},
|
|
466
|
-
extractDocstrings(tree, source) {
|
|
467
|
-
const result = /* @__PURE__ */ new Map();
|
|
468
|
-
const rootChildren = tree.rootNode.children;
|
|
469
|
-
for (let i = 0; i < rootChildren.length; i++) {
|
|
470
|
-
const child = rootChildren[i];
|
|
471
|
-
const name = extractPhpDeclarationName(child);
|
|
472
|
-
if (!name || name.startsWith("__namespace:")) continue;
|
|
473
|
-
const docstring = findPhpDocstring(rootChildren, i, source);
|
|
474
|
-
const members = /* @__PURE__ */ new Map();
|
|
475
|
-
const body = child.childForFieldName("body");
|
|
476
|
-
if (body) {
|
|
477
|
-
const bodyChildren = body.children;
|
|
478
|
-
for (let j = 0; j < bodyChildren.length; j++) {
|
|
479
|
-
const member = bodyChildren[j];
|
|
480
|
-
if (member.type !== "method_declaration") continue;
|
|
481
|
-
const memberName = member.childForFieldName("name")?.text;
|
|
482
|
-
if (!memberName) continue;
|
|
483
|
-
const memberDoc = findPhpDocstring(bodyChildren, j, source);
|
|
484
|
-
members.set(memberName, {
|
|
485
|
-
docstring: memberDoc,
|
|
486
|
-
declStartIndex: member.startIndex,
|
|
487
|
-
declColumn: member.startPosition.column
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
result.set(name, {
|
|
492
|
-
docstring,
|
|
493
|
-
declStartIndex: child.startIndex,
|
|
494
|
-
declColumn: child.startPosition.column,
|
|
495
|
-
members
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
return result;
|
|
499
|
-
}
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
// src/engine/merge-adapters/python.ts
|
|
503
|
-
function extractPythonDeclarationName(node) {
|
|
504
|
-
if (node.type === "class_definition" || node.type === "function_definition") {
|
|
505
|
-
const nameNode = node.childForFieldName("name");
|
|
506
|
-
return nameNode?.text ?? null;
|
|
507
|
-
}
|
|
508
|
-
if (node.type === "expression_statement") {
|
|
509
|
-
const firstChild = node.firstNamedChild;
|
|
510
|
-
if (firstChild?.type === "assignment") {
|
|
511
|
-
const left = firstChild.childForFieldName("left");
|
|
512
|
-
if (left?.type === "identifier" && left.text === "__all__") {
|
|
513
|
-
return "__all__";
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return null;
|
|
518
|
-
}
|
|
519
|
-
function extractPythonDocstring(bodyNode, source) {
|
|
520
|
-
for (const child of bodyNode.namedChildren) {
|
|
521
|
-
if (child.type === "expression_statement") {
|
|
522
|
-
const expr = child.firstNamedChild;
|
|
523
|
-
if (expr?.type === "string") {
|
|
524
|
-
const text = source.slice(child.startIndex, child.endIndex);
|
|
525
|
-
return { text, startIndex: child.startIndex, endIndex: child.endIndex };
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
break;
|
|
529
|
-
}
|
|
530
|
-
return null;
|
|
531
|
-
}
|
|
532
|
-
function collectPrecedingPythonComments(children, index, source) {
|
|
533
|
-
let lastIdx = -1;
|
|
534
|
-
let firstIdx = -1;
|
|
535
|
-
for (let k = index - 1; k >= 0; k--) {
|
|
536
|
-
const prev = children[k];
|
|
537
|
-
if (prev.type === "comment") {
|
|
538
|
-
if (lastIdx === -1) lastIdx = k;
|
|
539
|
-
firstIdx = k;
|
|
540
|
-
} else {
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
if (firstIdx === -1) return null;
|
|
545
|
-
const first = children[firstIdx];
|
|
546
|
-
const last = children[lastIdx];
|
|
547
|
-
return { text: source.slice(first.startIndex, last.endIndex), startIndex: first.startIndex, endIndex: last.endIndex };
|
|
548
|
-
}
|
|
549
|
-
var pythonMergeAdapter = {
|
|
550
|
-
language: "python",
|
|
551
|
-
grammarModule: "tree-sitter-python",
|
|
552
|
-
parseStatements(tree, source) {
|
|
553
|
-
const imports = [];
|
|
554
|
-
const importAnchors = [];
|
|
555
|
-
const statements = [];
|
|
556
|
-
for (const child of tree.rootNode.children) {
|
|
557
|
-
if (child.type === "comment") continue;
|
|
558
|
-
if (child.type === "import_statement" || child.type === "import_from_statement") {
|
|
559
|
-
const text = source.slice(child.startIndex, child.endIndex);
|
|
560
|
-
imports.push({ key: text.trim(), text });
|
|
561
|
-
importAnchors.push(text);
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
const declarationName = extractPythonDeclarationName(child);
|
|
565
|
-
const kind = declarationName ? "declaration" : "other";
|
|
566
|
-
statements.push({
|
|
567
|
-
key: declarationName,
|
|
568
|
-
kind,
|
|
569
|
-
text: source.slice(child.startIndex, child.endIndex)
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
return { imports, importAnchors, statements };
|
|
573
|
-
},
|
|
574
|
-
renderImports(imports) {
|
|
575
|
-
return imports.map((entry) => entry.text);
|
|
576
|
-
},
|
|
577
|
-
extractDocstrings(tree, source) {
|
|
578
|
-
const result = /* @__PURE__ */ new Map();
|
|
579
|
-
const rootChildren = tree.rootNode.children;
|
|
580
|
-
for (let i = 0; i < rootChildren.length; i++) {
|
|
581
|
-
const child = rootChildren[i];
|
|
582
|
-
const name = extractPythonDeclarationName(child);
|
|
583
|
-
if (!name) continue;
|
|
584
|
-
let docstring = null;
|
|
585
|
-
const body = child.childForFieldName("body");
|
|
586
|
-
if (body) {
|
|
587
|
-
docstring = extractPythonDocstring(body, source);
|
|
588
|
-
}
|
|
589
|
-
if (!docstring) {
|
|
590
|
-
docstring = collectPrecedingPythonComments(rootChildren, i, source);
|
|
591
|
-
}
|
|
592
|
-
const members = /* @__PURE__ */ new Map();
|
|
593
|
-
if (child.type === "class_definition" && body) {
|
|
594
|
-
for (const member of body.namedChildren) {
|
|
595
|
-
if (member.type !== "function_definition") continue;
|
|
596
|
-
const memberName = member.childForFieldName("name")?.text;
|
|
597
|
-
if (!memberName) continue;
|
|
598
|
-
let memberDoc = null;
|
|
599
|
-
const memberBody = member.childForFieldName("body");
|
|
600
|
-
if (memberBody) {
|
|
601
|
-
memberDoc = extractPythonDocstring(memberBody, source);
|
|
602
|
-
}
|
|
603
|
-
members.set(memberName, {
|
|
604
|
-
docstring: memberDoc,
|
|
605
|
-
declStartIndex: member.startIndex,
|
|
606
|
-
declColumn: member.startPosition.column
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
result.set(name, {
|
|
611
|
-
docstring,
|
|
612
|
-
declStartIndex: child.startIndex,
|
|
613
|
-
declColumn: child.startPosition.column,
|
|
614
|
-
members
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
return result;
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
|
|
621
|
-
// src/engine/merge-adapters/ruby.ts
|
|
622
|
-
function isRequireCall(node) {
|
|
623
|
-
if (node.type !== "call") return false;
|
|
624
|
-
const method = node.childForFieldName("method");
|
|
625
|
-
if (!method) return false;
|
|
626
|
-
return method.text === "require" || method.text === "require_relative";
|
|
627
|
-
}
|
|
628
|
-
function extractRubyDeclarationName(node) {
|
|
629
|
-
if (node.type !== "class" && node.type !== "module") return null;
|
|
630
|
-
const nameNode = node.childForFieldName("name");
|
|
631
|
-
return nameNode?.text ?? null;
|
|
632
|
-
}
|
|
633
|
-
function collectPrecedingRubyComments(children, index, source) {
|
|
634
|
-
let lastIdx = -1;
|
|
635
|
-
let firstIdx = -1;
|
|
636
|
-
for (let k = index - 1; k >= 0; k--) {
|
|
637
|
-
const prev = children[k];
|
|
638
|
-
const nodeType = prev.grammarType ?? prev.type;
|
|
639
|
-
if (nodeType === "comment") {
|
|
640
|
-
const text = source.slice(prev.startIndex, prev.endIndex);
|
|
641
|
-
if (text.startsWith("#")) {
|
|
642
|
-
if (lastIdx === -1) lastIdx = k;
|
|
643
|
-
firstIdx = k;
|
|
644
|
-
} else {
|
|
645
|
-
break;
|
|
646
|
-
}
|
|
647
|
-
} else {
|
|
648
|
-
break;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
if (firstIdx === -1) return null;
|
|
652
|
-
const first = children[firstIdx];
|
|
653
|
-
const last = children[lastIdx];
|
|
654
|
-
return { text: source.slice(first.startIndex, last.endIndex), startIndex: first.startIndex, endIndex: last.endIndex };
|
|
655
|
-
}
|
|
656
|
-
var rubyMergeAdapter = {
|
|
657
|
-
language: "ruby",
|
|
658
|
-
grammarModule: "tree-sitter-ruby",
|
|
659
|
-
parseStatements(tree, source) {
|
|
660
|
-
const imports = [];
|
|
661
|
-
const importAnchors = [];
|
|
662
|
-
const statements = [];
|
|
663
|
-
for (const child of tree.rootNode.children) {
|
|
664
|
-
if (child.type === "comment") continue;
|
|
665
|
-
const declarationName = extractRubyDeclarationName(child);
|
|
666
|
-
if (isRequireCall(child)) {
|
|
667
|
-
const text = source.slice(child.startIndex, child.endIndex);
|
|
668
|
-
imports.push({ key: text.trim(), text });
|
|
669
|
-
importAnchors.push(text);
|
|
670
|
-
continue;
|
|
671
|
-
}
|
|
672
|
-
const kind = declarationName ? "declaration" : "other";
|
|
673
|
-
statements.push({
|
|
674
|
-
key: declarationName,
|
|
675
|
-
kind,
|
|
676
|
-
text: source.slice(child.startIndex, child.endIndex)
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
return { imports, importAnchors, statements };
|
|
680
|
-
},
|
|
681
|
-
renderImports(imports) {
|
|
682
|
-
return imports.map((entry) => entry.text);
|
|
683
|
-
},
|
|
684
|
-
extractDocstrings(tree, source) {
|
|
685
|
-
const result = /* @__PURE__ */ new Map();
|
|
686
|
-
const rootChildren = tree.rootNode.children;
|
|
687
|
-
for (let i = 0; i < rootChildren.length; i++) {
|
|
688
|
-
const child = rootChildren[i];
|
|
689
|
-
const name = extractRubyDeclarationName(child);
|
|
690
|
-
if (!name) continue;
|
|
691
|
-
const docstring = collectPrecedingRubyComments(rootChildren, i, source);
|
|
692
|
-
const members = /* @__PURE__ */ new Map();
|
|
693
|
-
if (child.type === "class" || child.type === "module") {
|
|
694
|
-
const allClassChildren = child.children;
|
|
695
|
-
const body = child.childForFieldName("body");
|
|
696
|
-
let firstMethodDocFromClass = null;
|
|
697
|
-
for (let j = 0; j < allClassChildren.length; j++) {
|
|
698
|
-
if (allClassChildren[j] === body || allClassChildren[j].type === "body_statement") {
|
|
699
|
-
firstMethodDocFromClass = collectPrecedingRubyComments(allClassChildren, j, source);
|
|
700
|
-
break;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
if (body) {
|
|
704
|
-
const bodyChildren = body.children;
|
|
705
|
-
let firstMethodHandled = false;
|
|
706
|
-
for (let j = 0; j < bodyChildren.length; j++) {
|
|
707
|
-
const member = bodyChildren[j];
|
|
708
|
-
if (member.type !== "method") continue;
|
|
709
|
-
const memberName = member.childForFieldName("name")?.text;
|
|
710
|
-
if (!memberName) continue;
|
|
711
|
-
let memberDoc;
|
|
712
|
-
if (!firstMethodHandled && firstMethodDocFromClass) {
|
|
713
|
-
memberDoc = firstMethodDocFromClass;
|
|
714
|
-
firstMethodHandled = true;
|
|
715
|
-
} else {
|
|
716
|
-
memberDoc = collectPrecedingRubyComments(bodyChildren, j, source);
|
|
717
|
-
firstMethodHandled = true;
|
|
718
|
-
}
|
|
719
|
-
members.set(memberName, {
|
|
720
|
-
docstring: memberDoc,
|
|
721
|
-
declStartIndex: member.startIndex,
|
|
722
|
-
declColumn: member.startPosition.column
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
result.set(name, {
|
|
728
|
-
docstring,
|
|
729
|
-
declStartIndex: child.startIndex,
|
|
730
|
-
declColumn: child.startPosition.column,
|
|
731
|
-
members
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
return result;
|
|
735
|
-
}
|
|
736
|
-
};
|
|
737
|
-
|
|
738
|
-
// src/engine/merge-adapters/rust.ts
|
|
739
|
-
function declarationKey2(node) {
|
|
740
|
-
switch (node.type) {
|
|
741
|
-
case "struct_item":
|
|
742
|
-
case "enum_item":
|
|
743
|
-
case "function_item":
|
|
744
|
-
case "trait_item":
|
|
745
|
-
case "type_item":
|
|
746
|
-
case "const_item": {
|
|
747
|
-
return node.childForFieldName("name")?.text ?? null;
|
|
748
|
-
}
|
|
749
|
-
case "impl_item": {
|
|
750
|
-
const typeName = node.childForFieldName("type")?.text ?? "unknown";
|
|
751
|
-
const traitName = node.childForFieldName("trait")?.text;
|
|
752
|
-
return traitName ? `impl:${traitName}->${typeName}` : `impl:${typeName}`;
|
|
753
|
-
}
|
|
754
|
-
default:
|
|
755
|
-
return null;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
function collectPrecedingRustDocComments(children, index, source) {
|
|
759
|
-
let lastIdx = -1;
|
|
760
|
-
let firstIdx = -1;
|
|
761
|
-
for (let k = index - 1; k >= 0; k--) {
|
|
762
|
-
const prev = children[k];
|
|
763
|
-
if (prev.type === "line_comment") {
|
|
764
|
-
const text = source.slice(prev.startIndex, prev.endIndex);
|
|
765
|
-
if (text.startsWith("///")) {
|
|
766
|
-
if (lastIdx === -1) lastIdx = k;
|
|
767
|
-
firstIdx = k;
|
|
768
|
-
} else {
|
|
769
|
-
break;
|
|
770
|
-
}
|
|
771
|
-
} else {
|
|
772
|
-
break;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
if (firstIdx === -1) return null;
|
|
776
|
-
const first = children[firstIdx];
|
|
777
|
-
const last = children[lastIdx];
|
|
778
|
-
return { text: source.slice(first.startIndex, last.endIndex), startIndex: first.startIndex, endIndex: last.endIndex };
|
|
779
|
-
}
|
|
780
|
-
var rustMergeAdapter = {
|
|
781
|
-
language: "rust",
|
|
782
|
-
grammarModule: "tree-sitter-rust",
|
|
783
|
-
parseStatements(tree, source) {
|
|
784
|
-
const imports = [];
|
|
785
|
-
const importAnchors = [];
|
|
786
|
-
const statements = [];
|
|
787
|
-
for (const child of tree.rootNode.children) {
|
|
788
|
-
if (child.type === "comment") continue;
|
|
789
|
-
if (child.type === "use_declaration") {
|
|
790
|
-
const text = source.slice(child.startIndex, child.endIndex);
|
|
791
|
-
imports.push({ key: text.trim(), text });
|
|
792
|
-
importAnchors.push(text);
|
|
793
|
-
continue;
|
|
794
|
-
}
|
|
795
|
-
const key = declarationKey2(child);
|
|
796
|
-
statements.push({
|
|
797
|
-
key,
|
|
798
|
-
kind: key ? "declaration" : "other",
|
|
799
|
-
text: source.slice(child.startIndex, child.endIndex)
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
return { imports, importAnchors, statements };
|
|
803
|
-
},
|
|
804
|
-
renderImports(imports) {
|
|
805
|
-
return imports.map((entry) => entry.text);
|
|
806
|
-
},
|
|
807
|
-
extractDocstrings(tree, source) {
|
|
808
|
-
const result = /* @__PURE__ */ new Map();
|
|
809
|
-
const rootChildren = tree.rootNode.children;
|
|
810
|
-
for (let i = 0; i < rootChildren.length; i++) {
|
|
811
|
-
const child = rootChildren[i];
|
|
812
|
-
const key = declarationKey2(child);
|
|
813
|
-
if (!key) continue;
|
|
814
|
-
const docstring = collectPrecedingRustDocComments(rootChildren, i, source);
|
|
815
|
-
result.set(key, {
|
|
816
|
-
docstring,
|
|
817
|
-
declStartIndex: child.startIndex,
|
|
818
|
-
declColumn: child.startPosition.column,
|
|
819
|
-
members: /* @__PURE__ */ new Map()
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
return result;
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
|
|
826
|
-
// src/engine/merge-adapters/index.ts
|
|
827
|
-
var adapters = /* @__PURE__ */ new Map([
|
|
828
|
-
["go", goMergeAdapter],
|
|
829
|
-
["node", nodeMergeAdapter],
|
|
830
|
-
["php", phpMergeAdapter],
|
|
831
|
-
["python", pythonMergeAdapter],
|
|
832
|
-
["ruby", rubyMergeAdapter],
|
|
833
|
-
["rust", rustMergeAdapter]
|
|
834
|
-
]);
|
|
835
|
-
function getMergeAdapter(language) {
|
|
836
|
-
return adapters.get(language);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// src/engine/merger.ts
|
|
840
|
-
var parserCache = /* @__PURE__ */ new Map();
|
|
841
|
-
function findIgnoredRegions(source) {
|
|
842
|
-
const regions = [];
|
|
843
|
-
const startTag = "@oagen-ignore-start";
|
|
844
|
-
const endTag = "@oagen-ignore-end";
|
|
845
|
-
let searchFrom = 0;
|
|
846
|
-
while (true) {
|
|
847
|
-
const startIdx = source.indexOf(startTag, searchFrom);
|
|
848
|
-
if (startIdx === -1) break;
|
|
849
|
-
const endIdx = source.indexOf(endTag, startIdx + startTag.length);
|
|
850
|
-
if (endIdx === -1) break;
|
|
851
|
-
regions.push({ startIndex: startIdx, endIndex: endIdx + endTag.length });
|
|
852
|
-
searchFrom = endIdx + endTag.length;
|
|
853
|
-
}
|
|
854
|
-
return regions;
|
|
855
|
-
}
|
|
856
|
-
function buildIgnoredSymbolNames(docstrings, regions) {
|
|
857
|
-
if (regions.length === 0) return /* @__PURE__ */ new Set();
|
|
858
|
-
const ignored = /* @__PURE__ */ new Set();
|
|
859
|
-
for (const [name, info] of docstrings) {
|
|
860
|
-
if (regions.some((r) => info.declStartIndex >= r.startIndex && info.declStartIndex <= r.endIndex)) {
|
|
861
|
-
ignored.add(name);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
return ignored;
|
|
865
|
-
}
|
|
866
|
-
function hasGrammar(language) {
|
|
867
|
-
return getMergeAdapter(language) !== void 0;
|
|
868
|
-
}
|
|
869
|
-
async function getParser(language) {
|
|
870
|
-
const cached = parserCache.get(language);
|
|
871
|
-
if (cached) return cached;
|
|
872
|
-
const adapter = getMergeAdapter(language);
|
|
873
|
-
if (!adapter) {
|
|
874
|
-
throw new Error(
|
|
875
|
-
`No tree-sitter grammar configured for language "${language}". Add a merge adapter and install the corresponding tree-sitter grammar package.`
|
|
876
|
-
);
|
|
877
|
-
}
|
|
878
|
-
const mod = await import(adapter.grammarModule);
|
|
879
|
-
const exported = adapter.resolveGrammar ? adapter.resolveGrammar(mod) : mod;
|
|
880
|
-
const grammar = exported.default ?? exported;
|
|
881
|
-
const parser = new Parser();
|
|
882
|
-
parser.setLanguage(grammar);
|
|
883
|
-
parserCache.set(language, parser);
|
|
884
|
-
return parser;
|
|
885
|
-
}
|
|
886
|
-
async function extractStatements(source, language) {
|
|
887
|
-
const parser = await getParser(language);
|
|
888
|
-
const adapter = getMergeAdapter(language);
|
|
889
|
-
if (!adapter) {
|
|
890
|
-
throw new Error(`No merge adapter configured for language "${language}"`);
|
|
891
|
-
}
|
|
892
|
-
const tree = safeParse(parser, source);
|
|
893
|
-
return adapter.parseStatements(tree, source);
|
|
894
|
-
}
|
|
895
|
-
async function mergeIntoExisting(existingContent, generatedContent, language, header) {
|
|
896
|
-
const adapter = getMergeAdapter(language);
|
|
897
|
-
if (!adapter) {
|
|
898
|
-
throw new Error(`No merge adapter configured for language "${language}"`);
|
|
899
|
-
}
|
|
900
|
-
const existingStatements = await extractStatements(existingContent, language);
|
|
901
|
-
const generatedStatements = await extractStatements(generatedContent, language);
|
|
902
|
-
const existingKeys = /* @__PURE__ */ new Set();
|
|
903
|
-
const existingUnnamedTexts = /* @__PURE__ */ new Set();
|
|
904
|
-
const existingImportKeys = /* @__PURE__ */ new Set();
|
|
905
|
-
const existingReexports = /* @__PURE__ */ new Set();
|
|
906
|
-
let lastImportEndIndex = -1;
|
|
907
|
-
const existingImportedNames = /* @__PURE__ */ new Set();
|
|
908
|
-
for (const imp of existingStatements.imports) {
|
|
909
|
-
existingImportKeys.add(imp.key);
|
|
910
|
-
const braceMatch = imp.text.match(/\{([^}]+)\}/);
|
|
911
|
-
if (braceMatch) {
|
|
912
|
-
for (const name of braceMatch[1].split(",")) {
|
|
913
|
-
const trimmed = name.replace(/\btype\b/, "").trim();
|
|
914
|
-
if (trimmed) existingImportedNames.add(trimmed);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
for (const anchor of existingStatements.importAnchors) {
|
|
919
|
-
const linesBefore = existingContent.slice(0, existingContent.indexOf(anchor)).split("\n").length - 1;
|
|
920
|
-
const stmtLines = anchor.split("\n").length;
|
|
921
|
-
lastImportEndIndex = linesBefore + stmtLines - 1;
|
|
922
|
-
}
|
|
923
|
-
if (lastImportEndIndex === -1 && existingStatements.importInsertionAnchor) {
|
|
924
|
-
const linesBefore = existingContent.slice(0, existingContent.indexOf(existingStatements.importInsertionAnchor)).split("\n").length - 1;
|
|
925
|
-
const stmtLines = existingStatements.importInsertionAnchor.split("\n").length;
|
|
926
|
-
lastImportEndIndex = linesBefore + stmtLines - 1;
|
|
927
|
-
}
|
|
928
|
-
for (const stmt of existingStatements.statements) {
|
|
929
|
-
if (stmt.kind === "reexport") {
|
|
930
|
-
existingReexports.add(adapter.normalizeReexport ? adapter.normalizeReexport(stmt.text.trim()) : stmt.text.trim());
|
|
931
|
-
}
|
|
932
|
-
if (stmt.key) {
|
|
933
|
-
existingKeys.add(stmt.key);
|
|
934
|
-
} else {
|
|
935
|
-
existingUnnamedTexts.add(stmt.text.trim());
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
const headerLine = header.trim();
|
|
939
|
-
const newImports = [];
|
|
940
|
-
const toAppend = [];
|
|
941
|
-
let preserved = 0;
|
|
942
|
-
for (const imp of generatedStatements.imports) {
|
|
943
|
-
if (imp.text.trim() === headerLine) continue;
|
|
944
|
-
if (existingImportKeys.has(imp.key)) {
|
|
945
|
-
preserved++;
|
|
946
|
-
continue;
|
|
947
|
-
}
|
|
948
|
-
newImports.push(imp);
|
|
949
|
-
}
|
|
950
|
-
for (const stmt of generatedStatements.statements) {
|
|
951
|
-
if (stmt.text.trim() === headerLine) continue;
|
|
952
|
-
if (stmt.kind === "reexport") {
|
|
953
|
-
const normalizedText = adapter.normalizeReexport ? adapter.normalizeReexport(stmt.text.trim()) : stmt.text.trim();
|
|
954
|
-
if (existingReexports.has(normalizedText) || stmt.key !== null && existingKeys.has(stmt.key)) {
|
|
955
|
-
preserved++;
|
|
956
|
-
continue;
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
if (stmt.key && (existingKeys.has(stmt.key) || existingImportedNames.has(stmt.key))) {
|
|
960
|
-
preserved++;
|
|
961
|
-
continue;
|
|
962
|
-
}
|
|
963
|
-
if (!stmt.key) {
|
|
964
|
-
if (existingUnnamedTexts.has(stmt.text.trim())) {
|
|
965
|
-
preserved++;
|
|
966
|
-
continue;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
toAppend.push(stmt.text);
|
|
970
|
-
}
|
|
971
|
-
if (newImports.length === 0 && toAppend.length === 0) {
|
|
972
|
-
if (!adapter.extractMembers) {
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
let result = existingContent;
|
|
976
|
-
if (toAppend.length > 0) {
|
|
977
|
-
result = result.trimEnd() + "\n\n" + toAppend.join("\n\n") + "\n";
|
|
978
|
-
}
|
|
979
|
-
let deepAdded = 0;
|
|
980
|
-
if (adapter.extractMembers) {
|
|
981
|
-
const parser = await getParser(language);
|
|
982
|
-
const resultTree = safeParse(parser, result);
|
|
983
|
-
const generatedTree = safeParse(parser, generatedContent);
|
|
984
|
-
const resultSymbols = adapter.extractMembers(resultTree, result);
|
|
985
|
-
const generatedSymbols = adapter.extractMembers(generatedTree, generatedContent);
|
|
986
|
-
const existingTree = safeParse(parser, existingContent);
|
|
987
|
-
const existingDocs = adapter.extractDocstrings(existingTree, existingContent);
|
|
988
|
-
const deepIgnoredRegions = findIgnoredRegions(existingContent);
|
|
989
|
-
const deepIgnoredSymbols = buildIgnoredSymbolNames(existingDocs, deepIgnoredRegions);
|
|
990
|
-
const insertions = [];
|
|
991
|
-
for (const [symbolName, genSymbol] of generatedSymbols) {
|
|
992
|
-
if (deepIgnoredSymbols.has(symbolName)) continue;
|
|
993
|
-
const existSymbol = resultSymbols.get(symbolName);
|
|
994
|
-
if (!existSymbol) continue;
|
|
995
|
-
const existingMemberKeys = new Set(existSymbol.members.map((m) => m.key));
|
|
996
|
-
const newMembers = genSymbol.members.filter((m) => !existingMemberKeys.has(m.key));
|
|
997
|
-
if (newMembers.length > 0) {
|
|
998
|
-
const insertText = newMembers.map((m) => " " + m.text).join("\n");
|
|
999
|
-
insertions.push({ line: existSymbol.bodyEndLine, text: insertText });
|
|
1000
|
-
deepAdded += newMembers.length;
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
if (insertions.length > 0) {
|
|
1004
|
-
insertions.sort((a, b) => b.line - a.line);
|
|
1005
|
-
const resultLines = result.split("\n");
|
|
1006
|
-
for (const ins of insertions) {
|
|
1007
|
-
resultLines.splice(ins.line, 0, ins.text);
|
|
1008
|
-
}
|
|
1009
|
-
result = resultLines.join("\n");
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
if (newImports.length > 0 && (toAppend.length > 0 || deepAdded > 0)) {
|
|
1013
|
-
const renderedImports = adapter.renderImports ? adapter.renderImports(newImports) : newImports.map((entry) => entry.text);
|
|
1014
|
-
const lines = result.split("\n");
|
|
1015
|
-
const insertIdx = lastImportEndIndex + 1;
|
|
1016
|
-
lines.splice(insertIdx, 0, ...renderedImports);
|
|
1017
|
-
result = lines.join("\n");
|
|
1018
|
-
}
|
|
1019
|
-
let docstringUpdates = 0;
|
|
1020
|
-
{
|
|
1021
|
-
const parser = await getParser(language);
|
|
1022
|
-
const resultTree = safeParse(parser, result);
|
|
1023
|
-
const generatedTree = safeParse(parser, generatedContent);
|
|
1024
|
-
const resultDocs = adapter.extractDocstrings(resultTree, result);
|
|
1025
|
-
const generatedDocs = adapter.extractDocstrings(generatedTree, generatedContent);
|
|
1026
|
-
const docIgnoredRegions = findIgnoredRegions(result);
|
|
1027
|
-
const docIgnoredSymbols = buildIgnoredSymbolNames(resultDocs, docIgnoredRegions);
|
|
1028
|
-
const edits = [];
|
|
1029
|
-
for (const [symbolName, genInfo] of generatedDocs) {
|
|
1030
|
-
const existInfo = resultDocs.get(symbolName);
|
|
1031
|
-
if (!existInfo) continue;
|
|
1032
|
-
if (docIgnoredSymbols.has(symbolName)) continue;
|
|
1033
|
-
const genDoc = genInfo.docstring && genInfo.docstring.text.trim() !== headerLine ? genInfo.docstring : null;
|
|
1034
|
-
if (genDoc) {
|
|
1035
|
-
if (existInfo.docstring) {
|
|
1036
|
-
const isPreserved = existInfo.docstring.text.includes("@oagen-ignore");
|
|
1037
|
-
if (!isPreserved && existInfo.docstring.text !== genDoc.text) {
|
|
1038
|
-
edits.push({
|
|
1039
|
-
start: existInfo.docstring.startIndex,
|
|
1040
|
-
end: existInfo.docstring.endIndex,
|
|
1041
|
-
newText: genDoc.text
|
|
1042
|
-
});
|
|
1043
|
-
docstringUpdates++;
|
|
1044
|
-
}
|
|
1045
|
-
} else {
|
|
1046
|
-
const lineStart = existInfo.declStartIndex - existInfo.declColumn;
|
|
1047
|
-
const indent = " ".repeat(existInfo.declColumn);
|
|
1048
|
-
edits.push({
|
|
1049
|
-
start: lineStart,
|
|
1050
|
-
end: lineStart,
|
|
1051
|
-
newText: indent + genDoc.text + "\n"
|
|
1052
|
-
});
|
|
1053
|
-
docstringUpdates++;
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
for (const [memberName, genMember] of genInfo.members) {
|
|
1057
|
-
const existMember = existInfo.members.get(memberName);
|
|
1058
|
-
if (!existMember || !genMember.docstring) continue;
|
|
1059
|
-
if (existMember.docstring) {
|
|
1060
|
-
const isPreserved = existMember.docstring.text.includes("@oagen-ignore");
|
|
1061
|
-
if (!isPreserved && existMember.docstring.text !== genMember.docstring.text) {
|
|
1062
|
-
edits.push({
|
|
1063
|
-
start: existMember.docstring.startIndex,
|
|
1064
|
-
end: existMember.docstring.endIndex,
|
|
1065
|
-
newText: genMember.docstring.text
|
|
1066
|
-
});
|
|
1067
|
-
docstringUpdates++;
|
|
1068
|
-
}
|
|
1069
|
-
} else {
|
|
1070
|
-
const lineStart = existMember.declStartIndex - existMember.declColumn;
|
|
1071
|
-
const indent = " ".repeat(existMember.declColumn);
|
|
1072
|
-
edits.push({
|
|
1073
|
-
start: lineStart,
|
|
1074
|
-
end: lineStart,
|
|
1075
|
-
newText: indent + genMember.docstring.text + "\n"
|
|
1076
|
-
});
|
|
1077
|
-
docstringUpdates++;
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
if (edits.length > 0) {
|
|
1082
|
-
edits.sort((a, b) => b.start - a.start);
|
|
1083
|
-
for (const edit of edits) {
|
|
1084
|
-
result = result.slice(0, edit.start) + edit.newText + result.slice(edit.end);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
const importsActuallyAdded = toAppend.length > 0 || deepAdded > 0 ? adapter.renderImports ? adapter.renderImports(newImports).length : newImports.length : 0;
|
|
1089
|
-
const topLevelAdded = importsActuallyAdded + toAppend.length;
|
|
1090
|
-
const totalAdded = topLevelAdded + deepAdded;
|
|
1091
|
-
if (totalAdded === 0 && docstringUpdates === 0) {
|
|
1092
|
-
return { content: existingContent, added: 0, preserved, changed: false };
|
|
1093
|
-
}
|
|
1094
|
-
return {
|
|
1095
|
-
content: result,
|
|
1096
|
-
added: totalAdded,
|
|
1097
|
-
preserved,
|
|
1098
|
-
changed: true
|
|
1099
|
-
};
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
// src/engine/json-merge.ts
|
|
1103
|
-
function deepMergeJson(existing, generated) {
|
|
1104
|
-
if (typeof existing === "object" && existing !== null && !Array.isArray(existing) && typeof generated === "object" && generated !== null && !Array.isArray(generated)) {
|
|
1105
|
-
const merged = { ...existing };
|
|
1106
|
-
for (const [key, value] of Object.entries(generated)) {
|
|
1107
|
-
if (key in merged) {
|
|
1108
|
-
merged[key] = deepMergeJson(merged[key], value);
|
|
1109
|
-
} else {
|
|
1110
|
-
merged[key] = value;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
return merged;
|
|
1114
|
-
}
|
|
1115
|
-
return generated;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
// src/engine/writer.ts
|
|
1119
|
-
async function writeFiles(files, outputDir, options) {
|
|
1120
|
-
const result = { written: [], merged: [], skipped: [], identical: [], ignored: [] };
|
|
1121
|
-
const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
|
|
1122
|
-
const language = options?.language;
|
|
1123
|
-
const header = options?.header ?? "";
|
|
1124
|
-
for (const file of sorted) {
|
|
1125
|
-
const fullPath = path.join(outputDir, file.path);
|
|
1126
|
-
let existingContent = null;
|
|
1127
|
-
try {
|
|
1128
|
-
existingContent = await fs.readFile(fullPath, "utf-8");
|
|
1129
|
-
} catch {
|
|
1130
|
-
}
|
|
1131
|
-
if (existingContent === null) {
|
|
1132
|
-
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1133
|
-
await fs.writeFile(fullPath, file.content, "utf-8");
|
|
1134
|
-
result.written.push(file.path);
|
|
1135
|
-
continue;
|
|
1136
|
-
}
|
|
1137
|
-
if (file.skipIfExists) {
|
|
1138
|
-
if (header && !existingContent.startsWith(header)) {
|
|
1139
|
-
await fs.writeFile(fullPath, header + "\n\n" + existingContent, "utf-8");
|
|
1140
|
-
}
|
|
1141
|
-
result.skipped.push(file.path);
|
|
1142
|
-
continue;
|
|
1143
|
-
}
|
|
1144
|
-
if (existingContent.includes("@oagen-ignore-file")) {
|
|
1145
|
-
result.ignored.push(file.path);
|
|
1146
|
-
continue;
|
|
1147
|
-
}
|
|
1148
|
-
if (existingContent === file.content) {
|
|
1149
|
-
result.identical.push(file.path);
|
|
1150
|
-
continue;
|
|
1151
|
-
}
|
|
1152
|
-
if (file.path.endsWith(".json")) {
|
|
1153
|
-
try {
|
|
1154
|
-
const existingJson = JSON.parse(existingContent);
|
|
1155
|
-
const generatedJson = JSON.parse(file.content);
|
|
1156
|
-
const merged = deepMergeJson(existingJson, generatedJson);
|
|
1157
|
-
const mergedContent = JSON.stringify(merged, null, 2) + "\n";
|
|
1158
|
-
if (mergedContent === existingContent) {
|
|
1159
|
-
result.identical.push(file.path);
|
|
1160
|
-
} else {
|
|
1161
|
-
await fs.writeFile(fullPath, mergedContent, "utf-8");
|
|
1162
|
-
result.merged.push(file.path);
|
|
1163
|
-
}
|
|
1164
|
-
} catch {
|
|
1165
|
-
await fs.writeFile(fullPath, file.content, "utf-8");
|
|
1166
|
-
result.written.push(file.path);
|
|
1167
|
-
}
|
|
1168
|
-
continue;
|
|
1169
|
-
}
|
|
1170
|
-
if (language && hasGrammar(language)) {
|
|
1171
|
-
try {
|
|
1172
|
-
const mergeResult = await mergeIntoExisting(existingContent, file.content, language, header);
|
|
1173
|
-
let finalContent = mergeResult.content;
|
|
1174
|
-
if (header && !finalContent.startsWith(header)) {
|
|
1175
|
-
finalContent = header + "\n\n" + finalContent;
|
|
1176
|
-
}
|
|
1177
|
-
if (!mergeResult.changed && finalContent === existingContent) {
|
|
1178
|
-
result.identical.push(file.path);
|
|
1179
|
-
continue;
|
|
1180
|
-
}
|
|
1181
|
-
await fs.writeFile(fullPath, finalContent, "utf-8");
|
|
1182
|
-
result.merged.push(file.path);
|
|
1183
|
-
continue;
|
|
1184
|
-
} catch (err) {
|
|
1185
|
-
console.warn(
|
|
1186
|
-
`[oagen] AST merge failed for ${file.path}, falling back to overwrite.${err instanceof Error ? ` ${err.message}` : ""}`
|
|
1187
|
-
);
|
|
1188
|
-
await fs.writeFile(fullPath, file.content, "utf-8");
|
|
1189
|
-
result.written.push(file.path);
|
|
1190
|
-
continue;
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
result.skipped.push(file.path);
|
|
1194
|
-
}
|
|
1195
|
-
return result;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
// src/engine/integrate.ts
|
|
1199
|
-
function mapFilesForTargetIntegration(files, language) {
|
|
1200
|
-
const langPrefix = `${language}/`;
|
|
1201
|
-
return files.filter((f) => f.integrateTarget !== false).map((f) => {
|
|
1202
|
-
const stripped = f.path.startsWith(langPrefix) ? f.path.replace(langPrefix, "") : f.path;
|
|
1203
|
-
return {
|
|
1204
|
-
...f,
|
|
1205
|
-
skipIfExists: false,
|
|
1206
|
-
// Always merge in target — never hard-skip
|
|
1207
|
-
path: stripped
|
|
1208
|
-
};
|
|
1209
|
-
});
|
|
1210
|
-
}
|
|
1211
|
-
async function integrateGeneratedFiles(opts) {
|
|
1212
|
-
return writeFiles(mapFilesForTargetIntegration(opts.files, opts.language), opts.targetDir, {
|
|
1213
|
-
language: opts.language,
|
|
1214
|
-
header: opts.header
|
|
1215
|
-
});
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
// src/engine/orchestrator.ts
|
|
1219
|
-
async function generate(spec, emitter, options) {
|
|
1220
|
-
const { files: withHeaders, header } = generateFiles(spec, emitter, options);
|
|
1221
|
-
if (options.dryRun) {
|
|
1222
|
-
if (options.target) {
|
|
1223
|
-
console.log(`
|
|
1224
|
-
Target integration (${options.target}):`);
|
|
1225
|
-
for (const f of withHeaders) {
|
|
1226
|
-
console.log(` ${f.path}`);
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
return withHeaders;
|
|
1230
|
-
}
|
|
1231
|
-
const writeResult = await writeFiles(withHeaders, options.outputDir, {
|
|
1232
|
-
language: emitter.language,
|
|
1233
|
-
header
|
|
1234
|
-
});
|
|
1235
|
-
if (writeResult.merged.length > 0) {
|
|
1236
|
-
console.log(`Merged into ${writeResult.merged.length} existing files (additive only)`);
|
|
1237
|
-
}
|
|
1238
|
-
if (writeResult.ignored.length > 0) {
|
|
1239
|
-
console.log(`Ignored ${writeResult.ignored.length} files (@oagen-ignore-file)`);
|
|
1240
|
-
}
|
|
1241
|
-
if (options.target) {
|
|
1242
|
-
const targetResult = await integrateGeneratedFiles({
|
|
1243
|
-
files: withHeaders,
|
|
1244
|
-
language: emitter.language,
|
|
1245
|
-
targetDir: options.target,
|
|
1246
|
-
header
|
|
1247
|
-
});
|
|
1248
|
-
if (targetResult.written.length > 0) {
|
|
1249
|
-
console.log(`Target: created ${targetResult.written.length} new files`);
|
|
1250
|
-
}
|
|
1251
|
-
if (targetResult.merged.length > 0) {
|
|
1252
|
-
console.log(`Target: merged into ${targetResult.merged.length} existing files (additive only)`);
|
|
1253
|
-
}
|
|
1254
|
-
if (targetResult.skipped.length > 0) {
|
|
1255
|
-
console.log(`Target: skipped ${targetResult.skipped.length} files (excluded or no grammar)`);
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
return withHeaders;
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
// src/engine/registry.ts
|
|
1262
|
-
var emitters = /* @__PURE__ */ new Map();
|
|
1263
|
-
function registerEmitter(emitter) {
|
|
1264
|
-
emitters.set(emitter.language, emitter);
|
|
1265
|
-
}
|
|
1266
|
-
function getEmitter(language) {
|
|
1267
|
-
const emitter = emitters.get(language);
|
|
1268
|
-
if (!emitter) {
|
|
1269
|
-
const available = [...emitters.keys()].join(", ") || "(none)";
|
|
1270
|
-
throw new RegistryError(
|
|
1271
|
-
`Unknown language: ${language}. Available: ${available}`,
|
|
1272
|
-
`Register an emitter for "${language}" via registerEmitter() or check your oagen.config.ts plugin configuration.`
|
|
1273
|
-
);
|
|
1274
|
-
}
|
|
1275
|
-
return emitter;
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
// src/differ/classify.ts
|
|
1279
|
-
function classifyFieldChange(kind, fieldName, isRequired) {
|
|
1280
|
-
switch (kind) {
|
|
1281
|
-
case "field-added":
|
|
1282
|
-
return {
|
|
1283
|
-
kind,
|
|
1284
|
-
fieldName,
|
|
1285
|
-
classification: isRequired ? "breaking" : "additive"
|
|
1286
|
-
};
|
|
1287
|
-
case "field-removed":
|
|
1288
|
-
return { kind, fieldName, classification: "breaking" };
|
|
1289
|
-
case "field-type-changed":
|
|
1290
|
-
return { kind, fieldName, classification: "breaking" };
|
|
1291
|
-
case "field-required-changed":
|
|
1292
|
-
return {
|
|
1293
|
-
kind,
|
|
1294
|
-
fieldName,
|
|
1295
|
-
classification: isRequired ? "breaking" : "additive",
|
|
1296
|
-
details: isRequired ? "optional \u2192 required" : "required \u2192 optional"
|
|
1297
|
-
};
|
|
1298
|
-
case "field-access-changed":
|
|
1299
|
-
return { kind, fieldName, classification: "breaking" };
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
function classifyParamChange(kind, paramName, isRequired) {
|
|
1303
|
-
switch (kind) {
|
|
1304
|
-
case "param-added":
|
|
1305
|
-
return {
|
|
1306
|
-
kind,
|
|
1307
|
-
paramName,
|
|
1308
|
-
classification: isRequired ? "breaking" : "additive"
|
|
1309
|
-
};
|
|
1310
|
-
case "param-removed":
|
|
1311
|
-
return { kind, paramName, classification: "breaking" };
|
|
1312
|
-
case "param-type-changed":
|
|
1313
|
-
return { kind, paramName, classification: "breaking" };
|
|
1314
|
-
case "param-required-changed":
|
|
1315
|
-
return {
|
|
1316
|
-
kind,
|
|
1317
|
-
paramName,
|
|
1318
|
-
classification: isRequired ? "breaking" : "additive"
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
// src/differ/models.ts
|
|
1324
|
-
function diffModels(oldModels, newModels) {
|
|
1325
|
-
const changes = [];
|
|
1326
|
-
const oldByName = new Map(oldModels.map((m) => [m.name, m]));
|
|
1327
|
-
const newByName = new Map(newModels.map((m) => [m.name, m]));
|
|
1328
|
-
for (const [name] of newByName) {
|
|
1329
|
-
if (!oldByName.has(name)) {
|
|
1330
|
-
changes.push({ kind: "model-added", name, classification: "additive" });
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
for (const [name] of oldByName) {
|
|
1334
|
-
if (!newByName.has(name)) {
|
|
1335
|
-
changes.push({ kind: "model-removed", name, classification: "breaking" });
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
for (const [name, newModel] of newByName) {
|
|
1339
|
-
const oldModel = oldByName.get(name);
|
|
1340
|
-
if (!oldModel) continue;
|
|
1341
|
-
const fieldChanges = diffFields(oldModel, newModel);
|
|
1342
|
-
if (fieldChanges.length > 0) {
|
|
1343
|
-
const hasBreaking = fieldChanges.some((fc) => fc.classification === "breaking");
|
|
1344
|
-
changes.push({
|
|
1345
|
-
kind: "model-modified",
|
|
1346
|
-
name,
|
|
1347
|
-
fieldChanges,
|
|
1348
|
-
classification: hasBreaking ? "breaking" : "additive"
|
|
1349
|
-
});
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
return changes;
|
|
1353
|
-
}
|
|
1354
|
-
function diffFields(oldModel, newModel) {
|
|
1355
|
-
const changes = [];
|
|
1356
|
-
const oldByName = new Map(oldModel.fields.map((f) => [f.name, f]));
|
|
1357
|
-
const newByName = new Map(newModel.fields.map((f) => [f.name, f]));
|
|
1358
|
-
for (const [name, field] of newByName) {
|
|
1359
|
-
if (!oldByName.has(name)) {
|
|
1360
|
-
changes.push(classifyFieldChange("field-added", name, field.required));
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
for (const [name] of oldByName) {
|
|
1364
|
-
if (!newByName.has(name)) {
|
|
1365
|
-
changes.push(classifyFieldChange("field-removed", name));
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
for (const [name, newField] of newByName) {
|
|
1369
|
-
const oldField = oldByName.get(name);
|
|
1370
|
-
if (!oldField) continue;
|
|
1371
|
-
if (!typeRefsEqual(oldField.type, newField.type)) {
|
|
1372
|
-
changes.push({
|
|
1373
|
-
kind: "field-type-changed",
|
|
1374
|
-
fieldName: name,
|
|
1375
|
-
classification: "breaking",
|
|
1376
|
-
details: `type changed`
|
|
1377
|
-
});
|
|
1378
|
-
}
|
|
1379
|
-
if (oldField.required !== newField.required) {
|
|
1380
|
-
changes.push(classifyFieldChange("field-required-changed", name, newField.required));
|
|
1381
|
-
}
|
|
1382
|
-
if (!!oldField.readOnly !== !!newField.readOnly || !!oldField.writeOnly !== !!newField.writeOnly) {
|
|
1383
|
-
changes.push(classifyFieldChange("field-access-changed", name));
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
return changes;
|
|
1387
|
-
}
|
|
1388
|
-
function typeRefsEqual(a, b) {
|
|
1389
|
-
if (a.kind !== b.kind) return false;
|
|
1390
|
-
switch (a.kind) {
|
|
1391
|
-
case "primitive":
|
|
1392
|
-
return b.kind === "primitive" && a.type === b.type && (a.format ?? "") === (b.format ?? "");
|
|
1393
|
-
case "array":
|
|
1394
|
-
return b.kind === "array" && typeRefsEqual(a.items, b.items);
|
|
1395
|
-
case "model":
|
|
1396
|
-
return b.kind === "model" && a.name === b.name;
|
|
1397
|
-
case "enum":
|
|
1398
|
-
return b.kind === "enum" && a.name === b.name;
|
|
1399
|
-
case "literal":
|
|
1400
|
-
return b.kind === "literal" && a.value === b.value;
|
|
1401
|
-
case "nullable":
|
|
1402
|
-
return b.kind === "nullable" && typeRefsEqual(a.inner, b.inner);
|
|
1403
|
-
case "union": {
|
|
1404
|
-
if (b.kind !== "union") return false;
|
|
1405
|
-
if (a.variants.length !== b.variants.length) return false;
|
|
1406
|
-
if (!a.variants.every((v, i) => typeRefsEqual(v, b.variants[i]))) return false;
|
|
1407
|
-
if (!a.discriminator && !b.discriminator) return true;
|
|
1408
|
-
if (!a.discriminator || !b.discriminator) return false;
|
|
1409
|
-
if (a.discriminator.property !== b.discriminator.property) return false;
|
|
1410
|
-
const aKeys = Object.keys(a.discriminator.mapping).sort();
|
|
1411
|
-
const bKeys = Object.keys(b.discriminator.mapping).sort();
|
|
1412
|
-
if (aKeys.length !== bKeys.length) return false;
|
|
1413
|
-
return aKeys.every((k, i) => k === bKeys[i] && a.discriminator.mapping[k] === b.discriminator.mapping[k]);
|
|
1414
|
-
}
|
|
1415
|
-
case "map":
|
|
1416
|
-
return b.kind === "map" && typeRefsEqual(a.valueType, b.valueType);
|
|
1417
|
-
}
|
|
1418
|
-
return assertNever(a);
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
// src/differ/operations.ts
|
|
1422
|
-
function diffOperations(serviceName, oldOps, newOps) {
|
|
1423
|
-
const changes = [];
|
|
1424
|
-
const oldByName = new Map(oldOps.map((o) => [o.name, o]));
|
|
1425
|
-
const newByName = new Map(newOps.map((o) => [o.name, o]));
|
|
1426
|
-
for (const [name] of newByName) {
|
|
1427
|
-
if (!oldByName.has(name)) {
|
|
1428
|
-
changes.push({
|
|
1429
|
-
kind: "operation-added",
|
|
1430
|
-
serviceName,
|
|
1431
|
-
operationName: name,
|
|
1432
|
-
classification: "additive"
|
|
1433
|
-
});
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
for (const [name] of oldByName) {
|
|
1437
|
-
if (!newByName.has(name)) {
|
|
1438
|
-
changes.push({
|
|
1439
|
-
kind: "operation-removed",
|
|
1440
|
-
serviceName,
|
|
1441
|
-
operationName: name,
|
|
1442
|
-
classification: "breaking"
|
|
1443
|
-
});
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
for (const [name, newOp] of newByName) {
|
|
1447
|
-
const oldOp = oldByName.get(name);
|
|
1448
|
-
if (!oldOp) continue;
|
|
1449
|
-
const paramChanges = diffParams(oldOp, newOp);
|
|
1450
|
-
const responseChanged = !typeRefsEqual(oldOp.response, newOp.response);
|
|
1451
|
-
const requestBodyChanged = !requestBodiesEqual(oldOp.requestBody, newOp.requestBody);
|
|
1452
|
-
const httpMethodChanged = oldOp.httpMethod !== newOp.httpMethod;
|
|
1453
|
-
const pathChanged = oldOp.path !== newOp.path;
|
|
1454
|
-
const paginatedChanged = !!oldOp.pagination !== !!newOp.pagination;
|
|
1455
|
-
const injectIdempotencyKeyChanged = oldOp.injectIdempotencyKey !== newOp.injectIdempotencyKey;
|
|
1456
|
-
const errorsDiff = classifyErrorsChange(oldOp.errors, newOp.errors);
|
|
1457
|
-
const errorsChanged = errorsDiff !== "none";
|
|
1458
|
-
const hasChanges = paramChanges.length > 0 || responseChanged || requestBodyChanged || httpMethodChanged || pathChanged || paginatedChanged || injectIdempotencyKeyChanged || errorsChanged;
|
|
1459
|
-
if (hasChanges) {
|
|
1460
|
-
const paginatedBreaking = paginatedChanged && !newOp.pagination;
|
|
1461
|
-
const injectIdempotencyKeyBreaking = injectIdempotencyKeyChanged && !newOp.injectIdempotencyKey;
|
|
1462
|
-
const errorsBreaking = errorsDiff === "breaking";
|
|
1463
|
-
const hasBreaking = responseChanged || requestBodyChanged || httpMethodChanged || pathChanged || paginatedBreaking || injectIdempotencyKeyBreaking || errorsBreaking || paramChanges.some((pc) => pc.classification === "breaking");
|
|
1464
|
-
changes.push({
|
|
1465
|
-
kind: "operation-modified",
|
|
1466
|
-
serviceName,
|
|
1467
|
-
operationName: name,
|
|
1468
|
-
paramChanges,
|
|
1469
|
-
responseChanged,
|
|
1470
|
-
requestBodyChanged,
|
|
1471
|
-
httpMethodChanged,
|
|
1472
|
-
pathChanged,
|
|
1473
|
-
paginatedChanged,
|
|
1474
|
-
injectIdempotencyKeyChanged,
|
|
1475
|
-
errorsChanged,
|
|
1476
|
-
classification: hasBreaking ? "breaking" : "additive"
|
|
1477
|
-
});
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
return changes;
|
|
1481
|
-
}
|
|
1482
|
-
function requestBodiesEqual(a, b) {
|
|
1483
|
-
if (!a && !b) return true;
|
|
1484
|
-
if (!a || !b) return false;
|
|
1485
|
-
return typeRefsEqual(a, b);
|
|
1486
|
-
}
|
|
1487
|
-
function classifyErrorsChange(oldErrors, newErrors) {
|
|
1488
|
-
const oldByCode = new Map(oldErrors.map((e) => [e.statusCode, e]));
|
|
1489
|
-
const newByCode = new Map(newErrors.map((e) => [e.statusCode, e]));
|
|
1490
|
-
let hasAdditive = false;
|
|
1491
|
-
let hasBreaking = false;
|
|
1492
|
-
for (const code of newByCode.keys()) {
|
|
1493
|
-
if (!oldByCode.has(code)) hasAdditive = true;
|
|
1494
|
-
}
|
|
1495
|
-
for (const code of oldByCode.keys()) {
|
|
1496
|
-
if (!newByCode.has(code)) hasBreaking = true;
|
|
1497
|
-
}
|
|
1498
|
-
for (const [code, newErr] of newByCode) {
|
|
1499
|
-
const oldErr = oldByCode.get(code);
|
|
1500
|
-
if (!oldErr) continue;
|
|
1501
|
-
if (!oldErr.type && !newErr.type) continue;
|
|
1502
|
-
if (!oldErr.type || !newErr.type || !typeRefsEqual(oldErr.type, newErr.type)) {
|
|
1503
|
-
hasBreaking = true;
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
if (hasBreaking) return "breaking";
|
|
1507
|
-
if (hasAdditive) return "additive";
|
|
1508
|
-
return "none";
|
|
1509
|
-
}
|
|
1510
|
-
function diffParams(oldOp, newOp) {
|
|
1511
|
-
const changes = [];
|
|
1512
|
-
const allOldParams = [...oldOp.pathParams, ...oldOp.queryParams, ...oldOp.headerParams];
|
|
1513
|
-
const allNewParams = [...newOp.pathParams, ...newOp.queryParams, ...newOp.headerParams];
|
|
1514
|
-
const oldByName = new Map(allOldParams.map((p) => [p.name, p]));
|
|
1515
|
-
const newByName = new Map(allNewParams.map((p) => [p.name, p]));
|
|
1516
|
-
for (const [name, param] of newByName) {
|
|
1517
|
-
if (!oldByName.has(name)) {
|
|
1518
|
-
changes.push(classifyParamChange("param-added", name, param.required));
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
for (const [name] of oldByName) {
|
|
1522
|
-
if (!newByName.has(name)) {
|
|
1523
|
-
changes.push(classifyParamChange("param-removed", name));
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
for (const [name, newParam] of newByName) {
|
|
1527
|
-
const oldParam = oldByName.get(name);
|
|
1528
|
-
if (!oldParam) continue;
|
|
1529
|
-
if (!typeRefsEqual(oldParam.type, newParam.type)) {
|
|
1530
|
-
changes.push(classifyParamChange("param-type-changed", name));
|
|
1531
|
-
}
|
|
1532
|
-
if (oldParam.required !== newParam.required) {
|
|
1533
|
-
changes.push(classifyParamChange("param-required-changed", name, newParam.required));
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
return changes;
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
// src/differ/services.ts
|
|
1540
|
-
function diffServices(oldServices, newServices) {
|
|
1541
|
-
const changes = [];
|
|
1542
|
-
const oldByName = new Map(oldServices.map((s) => [s.name, s]));
|
|
1543
|
-
const newByName = new Map(newServices.map((s) => [s.name, s]));
|
|
1544
|
-
for (const [name] of newByName) {
|
|
1545
|
-
if (!oldByName.has(name)) {
|
|
1546
|
-
changes.push({ kind: "service-added", name, classification: "additive" });
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
for (const [name] of oldByName) {
|
|
1550
|
-
if (!newByName.has(name)) {
|
|
1551
|
-
changes.push({ kind: "service-removed", name, classification: "breaking" });
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
for (const [name, newService] of newByName) {
|
|
1555
|
-
const oldService = oldByName.get(name);
|
|
1556
|
-
if (!oldService) continue;
|
|
1557
|
-
changes.push(...diffOperations(name, oldService.operations, newService.operations));
|
|
1558
|
-
}
|
|
1559
|
-
return changes;
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
// src/differ/diff.ts
|
|
1563
|
-
function diffSpecs(oldSpec, newSpec) {
|
|
1564
|
-
const changes = [
|
|
1565
|
-
...diffModels(oldSpec.models, newSpec.models),
|
|
1566
|
-
...diffEnums(oldSpec.enums, newSpec.enums),
|
|
1567
|
-
...diffServices(oldSpec.services, newSpec.services)
|
|
1568
|
-
];
|
|
1569
|
-
return {
|
|
1570
|
-
oldVersion: oldSpec.version,
|
|
1571
|
-
newVersion: newSpec.version,
|
|
1572
|
-
changes,
|
|
1573
|
-
summary: summarize(changes)
|
|
1574
|
-
};
|
|
1575
|
-
}
|
|
1576
|
-
function diffEnums(oldEnums, newEnums) {
|
|
1577
|
-
const changes = [];
|
|
1578
|
-
const oldByName = new Map(oldEnums.map((e) => [e.name, e]));
|
|
1579
|
-
const newByName = new Map(newEnums.map((e) => [e.name, e]));
|
|
1580
|
-
for (const [name] of newByName) {
|
|
1581
|
-
if (!oldByName.has(name)) {
|
|
1582
|
-
changes.push({ kind: "enum-added", name, classification: "additive" });
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
for (const [name] of oldByName) {
|
|
1586
|
-
if (!newByName.has(name)) {
|
|
1587
|
-
changes.push({ kind: "enum-removed", name, classification: "breaking" });
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
for (const [name, newEnum] of newByName) {
|
|
1591
|
-
const oldEnum = oldByName.get(name);
|
|
1592
|
-
if (!oldEnum) continue;
|
|
1593
|
-
const valueChanges = diffEnumValues(oldEnum, newEnum);
|
|
1594
|
-
if (valueChanges.length > 0) {
|
|
1595
|
-
const hasBreaking = valueChanges.some((vc) => vc.classification === "breaking");
|
|
1596
|
-
changes.push({
|
|
1597
|
-
kind: "enum-modified",
|
|
1598
|
-
name,
|
|
1599
|
-
valueChanges,
|
|
1600
|
-
classification: hasBreaking ? "breaking" : "additive"
|
|
1601
|
-
});
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
return changes;
|
|
1605
|
-
}
|
|
1606
|
-
function diffEnumValues(oldEnum, newEnum) {
|
|
1607
|
-
const changes = [];
|
|
1608
|
-
const oldByName = new Map(oldEnum.values.map((v) => [v.name, v]));
|
|
1609
|
-
const newByName = new Map(newEnum.values.map((v) => [v.name, v]));
|
|
1610
|
-
for (const [name] of newByName) {
|
|
1611
|
-
if (!oldByName.has(name)) {
|
|
1612
|
-
changes.push({ kind: "value-added", valueName: name, classification: "additive" });
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
for (const [name] of oldByName) {
|
|
1616
|
-
if (!newByName.has(name)) {
|
|
1617
|
-
changes.push({ kind: "value-removed", valueName: name, classification: "breaking" });
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
for (const [name, newVal] of newByName) {
|
|
1621
|
-
const oldVal = oldByName.get(name);
|
|
1622
|
-
if (!oldVal) continue;
|
|
1623
|
-
if (oldVal.value !== newVal.value) {
|
|
1624
|
-
changes.push({
|
|
1625
|
-
kind: "value-changed",
|
|
1626
|
-
valueName: name,
|
|
1627
|
-
classification: "breaking",
|
|
1628
|
-
details: `value changed from '${oldVal.value}' to '${newVal.value}'`
|
|
1629
|
-
});
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
return changes;
|
|
1633
|
-
}
|
|
1634
|
-
function summarize(changes) {
|
|
1635
|
-
let added = 0;
|
|
1636
|
-
let removed = 0;
|
|
1637
|
-
let modified = 0;
|
|
1638
|
-
let breaking = 0;
|
|
1639
|
-
let additive = 0;
|
|
1640
|
-
for (const c of changes) {
|
|
1641
|
-
if (c.kind.endsWith("-added")) added++;
|
|
1642
|
-
else if (c.kind.endsWith("-removed")) removed++;
|
|
1643
|
-
else if (c.kind.endsWith("-modified")) modified++;
|
|
1644
|
-
if (c.classification === "breaking") breaking++;
|
|
1645
|
-
else additive++;
|
|
1646
|
-
}
|
|
1647
|
-
return { added, removed, modified, breaking, additive };
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
export {
|
|
1651
|
-
buildEmitterContext,
|
|
1652
|
-
generateAllFiles,
|
|
1653
|
-
applyFileHeaders,
|
|
1654
|
-
generateFiles,
|
|
1655
|
-
mapFilesForTargetIntegration,
|
|
1656
|
-
integrateGeneratedFiles,
|
|
1657
|
-
generate,
|
|
1658
|
-
registerEmitter,
|
|
1659
|
-
getEmitter,
|
|
1660
|
-
diffSpecs
|
|
1661
|
-
};
|