@abapify/ts-xsd 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -211
- package/dist/codegen/cli.d.mts +1 -0
- package/dist/codegen/cli.mjs +137 -0
- package/dist/codegen/cli.mjs.map +1 -0
- package/dist/generate-Br6rW6sE.mjs +262 -0
- package/dist/generate-Br6rW6sE.mjs.map +1 -0
- package/dist/generators/index.d.mts +197 -0
- package/dist/generators/index.d.mts.map +1 -0
- package/dist/generators/index.mjs +635 -0
- package/dist/generators/index.mjs.map +1 -0
- package/dist/index.d.mts +1193 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1923 -0
- package/dist/index.mjs.map +1 -0
- package/dist/runner-BSGqj8LM.mjs +1822 -0
- package/dist/runner-BSGqj8LM.mjs.map +1 -0
- package/dist/runner-D1RCU_go.d.mts +741 -0
- package/dist/runner-D1RCU_go.d.mts.map +1 -0
- package/dist/types-Ch0GQQ5n.mjs +527 -0
- package/dist/types-Ch0GQQ5n.mjs.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,1822 @@
|
|
|
1
|
+
import { DOMParser } from "@xmldom/xmldom";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region src/xml/dom-utils.ts
|
|
6
|
+
/**
|
|
7
|
+
* Type guard to check if a Node is an Element
|
|
8
|
+
*/
|
|
9
|
+
function isElement(node) {
|
|
10
|
+
return node.nodeType === 1;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get attribute value, handling namespaced attributes
|
|
14
|
+
* Tries direct attribute first, then searches by local name
|
|
15
|
+
*/
|
|
16
|
+
function getAttributeValue(node, name) {
|
|
17
|
+
if (node.hasAttribute(name)) return node.getAttribute(name);
|
|
18
|
+
for (let i = 0; i < node.attributes.length; i++) {
|
|
19
|
+
const attr = node.attributes[i];
|
|
20
|
+
if ((attr.localName || attr.name.split(":").pop()) === name) return attr.value;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get child elements by local name (namespace-agnostic)
|
|
26
|
+
*/
|
|
27
|
+
function getChildElements(parent, name) {
|
|
28
|
+
const result = [];
|
|
29
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
30
|
+
const child = parent.childNodes[i];
|
|
31
|
+
if (isElement(child)) {
|
|
32
|
+
if ((child.localName || child.tagName.split(":").pop()) === name) result.push(child);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get all child elements (any name)
|
|
39
|
+
*/
|
|
40
|
+
function getAllChildElements(parent) {
|
|
41
|
+
const result = [];
|
|
42
|
+
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
43
|
+
const child = parent.childNodes[i];
|
|
44
|
+
if (isElement(child)) result.push(child);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get text content of an element (concatenates all text nodes)
|
|
50
|
+
*/
|
|
51
|
+
function getTextContent(node) {
|
|
52
|
+
let text = "";
|
|
53
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
54
|
+
const child = node.childNodes[i];
|
|
55
|
+
if (child.nodeType === 3) text += child.textContent || "";
|
|
56
|
+
}
|
|
57
|
+
return text.trim();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get local name of an element (strips namespace prefix)
|
|
61
|
+
*/
|
|
62
|
+
function getLocalName(node) {
|
|
63
|
+
return node.localName || node.tagName.split(":").pop() || node.tagName;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/xsd/parse.ts
|
|
68
|
+
/**
|
|
69
|
+
* XSD Parser - Parse XSD documents to typed Schema objects
|
|
70
|
+
*
|
|
71
|
+
* This parser understands XSD semantics and produces correctly typed output
|
|
72
|
+
* matching the W3C XMLSchema.xsd specification.
|
|
73
|
+
*
|
|
74
|
+
* No intermediate JSON - direct XSD to typed objects.
|
|
75
|
+
*/
|
|
76
|
+
/**
|
|
77
|
+
* Parse an XSD string to a typed Schema object
|
|
78
|
+
*/
|
|
79
|
+
function parseXsd(xml) {
|
|
80
|
+
const root = new DOMParser().parseFromString(xml, "text/xml").documentElement;
|
|
81
|
+
if (!root || getLocalName(root) !== "schema") throw new Error("Invalid XSD: root element must be xs:schema");
|
|
82
|
+
return parseSchema(root);
|
|
83
|
+
}
|
|
84
|
+
function parseSchema(el) {
|
|
85
|
+
const schema = {};
|
|
86
|
+
copyXmlns(el, schema);
|
|
87
|
+
copyAttr(el, schema, "id");
|
|
88
|
+
copyAttr(el, schema, "targetNamespace");
|
|
89
|
+
copyAttr(el, schema, "version");
|
|
90
|
+
copyAttr(el, schema, "finalDefault");
|
|
91
|
+
copyAttr(el, schema, "blockDefault");
|
|
92
|
+
copyAttr(el, schema, "attributeFormDefault");
|
|
93
|
+
copyAttr(el, schema, "elementFormDefault");
|
|
94
|
+
copyAttr(el, schema, "defaultAttributes");
|
|
95
|
+
copyAttr(el, schema, "xpathDefaultNamespace");
|
|
96
|
+
copyAttr(el, schema, "xml:lang");
|
|
97
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
98
|
+
case "annotation":
|
|
99
|
+
pushTo(schema, "annotation", parseAnnotation(child));
|
|
100
|
+
break;
|
|
101
|
+
case "include":
|
|
102
|
+
pushTo(schema, "include", parseInclude(child));
|
|
103
|
+
break;
|
|
104
|
+
case "import":
|
|
105
|
+
pushTo(schema, "import", parseImport(child));
|
|
106
|
+
break;
|
|
107
|
+
case "redefine":
|
|
108
|
+
pushTo(schema, "redefine", parseRedefine(child));
|
|
109
|
+
break;
|
|
110
|
+
case "override":
|
|
111
|
+
pushTo(schema, "override", parseOverride(child));
|
|
112
|
+
break;
|
|
113
|
+
case "element":
|
|
114
|
+
pushTo(schema, "element", parseTopLevelElement(child));
|
|
115
|
+
break;
|
|
116
|
+
case "attribute":
|
|
117
|
+
pushTo(schema, "attribute", parseTopLevelAttribute(child));
|
|
118
|
+
break;
|
|
119
|
+
case "simpleType":
|
|
120
|
+
pushTo(schema, "simpleType", parseTopLevelSimpleType(child));
|
|
121
|
+
break;
|
|
122
|
+
case "complexType":
|
|
123
|
+
pushTo(schema, "complexType", parseTopLevelComplexType(child));
|
|
124
|
+
break;
|
|
125
|
+
case "group":
|
|
126
|
+
pushTo(schema, "group", parseNamedGroup(child));
|
|
127
|
+
break;
|
|
128
|
+
case "attributeGroup":
|
|
129
|
+
pushTo(schema, "attributeGroup", parseNamedAttributeGroup(child));
|
|
130
|
+
break;
|
|
131
|
+
case "notation":
|
|
132
|
+
pushTo(schema, "notation", parseNotation(child));
|
|
133
|
+
break;
|
|
134
|
+
case "defaultOpenContent": schema.defaultOpenContent = parseDefaultOpenContent(child);
|
|
135
|
+
}
|
|
136
|
+
return schema;
|
|
137
|
+
}
|
|
138
|
+
function parseAnnotation(el) {
|
|
139
|
+
const result = {};
|
|
140
|
+
copyAttr(el, result, "id");
|
|
141
|
+
for (const child of getAllChildElements(el)) {
|
|
142
|
+
const name = getLocalName(child);
|
|
143
|
+
if (name === "documentation") pushTo(result, "documentation", parseDocumentation(child));
|
|
144
|
+
else if (name === "appinfo") pushTo(result, "appinfo", parseAppinfo(child));
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
function parseDocumentation(el) {
|
|
149
|
+
const result = {};
|
|
150
|
+
copyAttr(el, result, "source");
|
|
151
|
+
copyAttr(el, result, "xml:lang");
|
|
152
|
+
const text = getTextContent(el);
|
|
153
|
+
if (text) result._text = text;
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
function parseAppinfo(el) {
|
|
157
|
+
const result = {};
|
|
158
|
+
copyAttr(el, result, "source");
|
|
159
|
+
const text = getTextContent(el);
|
|
160
|
+
if (text) result._text = text;
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
function parseInclude(el) {
|
|
164
|
+
const result = { schemaLocation: "" };
|
|
165
|
+
copyAttr(el, result, "id");
|
|
166
|
+
copyAttr(el, result, "schemaLocation");
|
|
167
|
+
parseAnnotationChild(el, result);
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
function parseImport(el) {
|
|
171
|
+
const result = {};
|
|
172
|
+
copyAttr(el, result, "id");
|
|
173
|
+
copyAttr(el, result, "namespace");
|
|
174
|
+
copyAttr(el, result, "schemaLocation");
|
|
175
|
+
parseAnnotationChild(el, result);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
function parseRedefine(el) {
|
|
179
|
+
const result = { schemaLocation: "" };
|
|
180
|
+
copyAttr(el, result, "id");
|
|
181
|
+
copyAttr(el, result, "schemaLocation");
|
|
182
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
183
|
+
case "annotation":
|
|
184
|
+
pushTo(result, "annotation", parseAnnotation(child));
|
|
185
|
+
break;
|
|
186
|
+
case "simpleType":
|
|
187
|
+
pushTo(result, "simpleType", parseTopLevelSimpleType(child));
|
|
188
|
+
break;
|
|
189
|
+
case "complexType":
|
|
190
|
+
pushTo(result, "complexType", parseTopLevelComplexType(child));
|
|
191
|
+
break;
|
|
192
|
+
case "group":
|
|
193
|
+
pushTo(result, "group", parseNamedGroup(child));
|
|
194
|
+
break;
|
|
195
|
+
case "attributeGroup": pushTo(result, "attributeGroup", parseNamedAttributeGroup(child));
|
|
196
|
+
}
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
function parseOverride(el) {
|
|
200
|
+
const result = { schemaLocation: "" };
|
|
201
|
+
copyAttr(el, result, "id");
|
|
202
|
+
copyAttr(el, result, "schemaLocation");
|
|
203
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
204
|
+
case "annotation":
|
|
205
|
+
pushTo(result, "annotation", parseAnnotation(child));
|
|
206
|
+
break;
|
|
207
|
+
case "simpleType":
|
|
208
|
+
pushTo(result, "simpleType", parseTopLevelSimpleType(child));
|
|
209
|
+
break;
|
|
210
|
+
case "complexType":
|
|
211
|
+
pushTo(result, "complexType", parseTopLevelComplexType(child));
|
|
212
|
+
break;
|
|
213
|
+
case "group":
|
|
214
|
+
pushTo(result, "group", parseNamedGroup(child));
|
|
215
|
+
break;
|
|
216
|
+
case "attributeGroup":
|
|
217
|
+
pushTo(result, "attributeGroup", parseNamedAttributeGroup(child));
|
|
218
|
+
break;
|
|
219
|
+
case "element":
|
|
220
|
+
pushTo(result, "element", parseTopLevelElement(child));
|
|
221
|
+
break;
|
|
222
|
+
case "attribute":
|
|
223
|
+
pushTo(result, "attribute", parseTopLevelAttribute(child));
|
|
224
|
+
break;
|
|
225
|
+
case "notation": pushTo(result, "notation", parseNotation(child));
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
function parseTopLevelElement(el) {
|
|
230
|
+
const result = { name: "" };
|
|
231
|
+
copyAttr(el, result, "id");
|
|
232
|
+
copyAttr(el, result, "name");
|
|
233
|
+
copyAttr(el, result, "type");
|
|
234
|
+
copyAttr(el, result, "substitutionGroup");
|
|
235
|
+
copyAttr(el, result, "default");
|
|
236
|
+
copyAttr(el, result, "fixed");
|
|
237
|
+
copyBoolAttr(el, result, "nillable");
|
|
238
|
+
copyBoolAttr(el, result, "abstract");
|
|
239
|
+
copyAttr(el, result, "final");
|
|
240
|
+
copyAttr(el, result, "block");
|
|
241
|
+
parseAnnotationChild(el, result);
|
|
242
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
243
|
+
case "simpleType":
|
|
244
|
+
result.simpleType = parseLocalSimpleType(child);
|
|
245
|
+
break;
|
|
246
|
+
case "complexType":
|
|
247
|
+
result.complexType = parseLocalComplexType(child);
|
|
248
|
+
break;
|
|
249
|
+
case "unique":
|
|
250
|
+
pushTo(result, "unique", parseUnique(child));
|
|
251
|
+
break;
|
|
252
|
+
case "key":
|
|
253
|
+
pushTo(result, "key", parseKey(child));
|
|
254
|
+
break;
|
|
255
|
+
case "keyref":
|
|
256
|
+
pushTo(result, "keyref", parseKeyref(child));
|
|
257
|
+
break;
|
|
258
|
+
case "alternative": pushTo(result, "alternative", parseAlternative(child));
|
|
259
|
+
}
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
function parseLocalElement(el) {
|
|
263
|
+
const result = {};
|
|
264
|
+
copyAttr(el, result, "id");
|
|
265
|
+
copyAttr(el, result, "name");
|
|
266
|
+
copyAttr(el, result, "ref");
|
|
267
|
+
copyAttr(el, result, "type");
|
|
268
|
+
copyAttr(el, result, "minOccurs");
|
|
269
|
+
copyAttr(el, result, "maxOccurs");
|
|
270
|
+
copyAttr(el, result, "default");
|
|
271
|
+
copyAttr(el, result, "fixed");
|
|
272
|
+
copyBoolAttr(el, result, "nillable");
|
|
273
|
+
copyAttr(el, result, "block");
|
|
274
|
+
copyAttr(el, result, "form");
|
|
275
|
+
copyAttr(el, result, "targetNamespace");
|
|
276
|
+
parseAnnotationChild(el, result);
|
|
277
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
278
|
+
case "simpleType":
|
|
279
|
+
result.simpleType = parseLocalSimpleType(child);
|
|
280
|
+
break;
|
|
281
|
+
case "complexType":
|
|
282
|
+
result.complexType = parseLocalComplexType(child);
|
|
283
|
+
break;
|
|
284
|
+
case "unique":
|
|
285
|
+
pushTo(result, "unique", parseUnique(child));
|
|
286
|
+
break;
|
|
287
|
+
case "key":
|
|
288
|
+
pushTo(result, "key", parseKey(child));
|
|
289
|
+
break;
|
|
290
|
+
case "keyref":
|
|
291
|
+
pushTo(result, "keyref", parseKeyref(child));
|
|
292
|
+
break;
|
|
293
|
+
case "alternative": pushTo(result, "alternative", parseAlternative(child));
|
|
294
|
+
}
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
function parseTopLevelAttribute(el) {
|
|
298
|
+
const result = { name: "" };
|
|
299
|
+
copyAttr(el, result, "id");
|
|
300
|
+
copyAttr(el, result, "name");
|
|
301
|
+
copyAttr(el, result, "type");
|
|
302
|
+
copyAttr(el, result, "default");
|
|
303
|
+
copyAttr(el, result, "fixed");
|
|
304
|
+
copyBoolAttr(el, result, "inheritable");
|
|
305
|
+
parseAnnotationChild(el, result);
|
|
306
|
+
for (const child of getAllChildElements(el)) if (getLocalName(child) === "simpleType") result.simpleType = parseLocalSimpleType(child);
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
function parseLocalAttribute(el) {
|
|
310
|
+
const result = {};
|
|
311
|
+
copyAttr(el, result, "id");
|
|
312
|
+
copyAttr(el, result, "name");
|
|
313
|
+
copyAttr(el, result, "ref");
|
|
314
|
+
copyAttr(el, result, "type");
|
|
315
|
+
copyAttr(el, result, "use");
|
|
316
|
+
copyAttr(el, result, "default");
|
|
317
|
+
copyAttr(el, result, "fixed");
|
|
318
|
+
copyAttr(el, result, "form");
|
|
319
|
+
copyAttr(el, result, "targetNamespace");
|
|
320
|
+
copyBoolAttr(el, result, "inheritable");
|
|
321
|
+
parseAnnotationChild(el, result);
|
|
322
|
+
for (const child of getAllChildElements(el)) if (getLocalName(child) === "simpleType") result.simpleType = parseLocalSimpleType(child);
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
function parseTopLevelComplexType(el) {
|
|
326
|
+
const result = { name: "" };
|
|
327
|
+
copyAttr(el, result, "id");
|
|
328
|
+
copyAttr(el, result, "name");
|
|
329
|
+
copyBoolAttr(el, result, "mixed");
|
|
330
|
+
copyBoolAttr(el, result, "abstract");
|
|
331
|
+
copyAttr(el, result, "final");
|
|
332
|
+
copyAttr(el, result, "block");
|
|
333
|
+
copyBoolAttr(el, result, "defaultAttributesApply");
|
|
334
|
+
parseAnnotationChild(el, result);
|
|
335
|
+
parseComplexTypeContent(el, result);
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
function parseLocalComplexType(el) {
|
|
339
|
+
const result = {};
|
|
340
|
+
copyAttr(el, result, "id");
|
|
341
|
+
copyBoolAttr(el, result, "mixed");
|
|
342
|
+
parseAnnotationChild(el, result);
|
|
343
|
+
parseComplexTypeContent(el, result);
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
function parseComplexTypeContent(el, result) {
|
|
347
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
348
|
+
case "simpleContent":
|
|
349
|
+
result.simpleContent = parseSimpleContent(child);
|
|
350
|
+
break;
|
|
351
|
+
case "complexContent":
|
|
352
|
+
result.complexContent = parseComplexContent(child);
|
|
353
|
+
break;
|
|
354
|
+
case "openContent":
|
|
355
|
+
result.openContent = parseOpenContent(child);
|
|
356
|
+
break;
|
|
357
|
+
case "group":
|
|
358
|
+
result.group = parseGroupRef(child);
|
|
359
|
+
break;
|
|
360
|
+
case "all":
|
|
361
|
+
result.all = parseAll(child);
|
|
362
|
+
break;
|
|
363
|
+
case "choice":
|
|
364
|
+
result.choice = parseExplicitGroup(child);
|
|
365
|
+
break;
|
|
366
|
+
case "sequence":
|
|
367
|
+
result.sequence = parseExplicitGroup(child);
|
|
368
|
+
break;
|
|
369
|
+
case "attribute":
|
|
370
|
+
pushTo(result, "attribute", parseLocalAttribute(child));
|
|
371
|
+
break;
|
|
372
|
+
case "attributeGroup":
|
|
373
|
+
pushTo(result, "attributeGroup", parseAttributeGroupRef(child));
|
|
374
|
+
break;
|
|
375
|
+
case "anyAttribute":
|
|
376
|
+
result.anyAttribute = parseAnyAttribute(child);
|
|
377
|
+
break;
|
|
378
|
+
case "assert": pushTo(result, "assert", parseAssertion(child));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function parseTopLevelSimpleType(el) {
|
|
382
|
+
const result = { name: "" };
|
|
383
|
+
copyAttr(el, result, "id");
|
|
384
|
+
copyAttr(el, result, "name");
|
|
385
|
+
copyAttr(el, result, "final");
|
|
386
|
+
parseAnnotationChild(el, result);
|
|
387
|
+
parseSimpleTypeContent(el, result);
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
function parseLocalSimpleType(el) {
|
|
391
|
+
const result = {};
|
|
392
|
+
copyAttr(el, result, "id");
|
|
393
|
+
parseAnnotationChild(el, result);
|
|
394
|
+
parseSimpleTypeContent(el, result);
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
function parseSimpleTypeContent(el, result) {
|
|
398
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
399
|
+
case "restriction":
|
|
400
|
+
result.restriction = parseSimpleTypeRestriction(child);
|
|
401
|
+
break;
|
|
402
|
+
case "list":
|
|
403
|
+
result.list = parseList(child);
|
|
404
|
+
break;
|
|
405
|
+
case "union": result.union = parseUnion(child);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function parseSimpleTypeRestriction(el) {
|
|
409
|
+
const result = {};
|
|
410
|
+
copyAttr(el, result, "id");
|
|
411
|
+
copyAttr(el, result, "base");
|
|
412
|
+
parseAnnotationChild(el, result);
|
|
413
|
+
for (const child of getAllChildElements(el)) {
|
|
414
|
+
const name = getLocalName(child);
|
|
415
|
+
switch (name) {
|
|
416
|
+
case "simpleType":
|
|
417
|
+
result.simpleType = parseLocalSimpleType(child);
|
|
418
|
+
break;
|
|
419
|
+
case "minExclusive":
|
|
420
|
+
case "minInclusive":
|
|
421
|
+
case "maxExclusive":
|
|
422
|
+
case "maxInclusive":
|
|
423
|
+
case "totalDigits":
|
|
424
|
+
case "fractionDigits":
|
|
425
|
+
case "length":
|
|
426
|
+
case "minLength":
|
|
427
|
+
case "maxLength":
|
|
428
|
+
case "enumeration":
|
|
429
|
+
case "whiteSpace":
|
|
430
|
+
case "explicitTimezone":
|
|
431
|
+
pushTo(result, name, parseFacet(child));
|
|
432
|
+
break;
|
|
433
|
+
case "pattern":
|
|
434
|
+
pushTo(result, "pattern", parsePattern(child));
|
|
435
|
+
break;
|
|
436
|
+
case "assertion": pushTo(result, "assertion", parseAssertion(child));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
function parseList(el) {
|
|
442
|
+
const result = {};
|
|
443
|
+
copyAttr(el, result, "id");
|
|
444
|
+
copyAttr(el, result, "itemType");
|
|
445
|
+
parseAnnotationChild(el, result);
|
|
446
|
+
for (const child of getAllChildElements(el)) if (getLocalName(child) === "simpleType") result.simpleType = parseLocalSimpleType(child);
|
|
447
|
+
return result;
|
|
448
|
+
}
|
|
449
|
+
function parseUnion(el) {
|
|
450
|
+
const result = {};
|
|
451
|
+
copyAttr(el, result, "id");
|
|
452
|
+
copyAttr(el, result, "memberTypes");
|
|
453
|
+
parseAnnotationChild(el, result);
|
|
454
|
+
for (const child of getAllChildElements(el)) if (getLocalName(child) === "simpleType") pushTo(result, "simpleType", parseLocalSimpleType(child));
|
|
455
|
+
return result;
|
|
456
|
+
}
|
|
457
|
+
function parseFacet(el) {
|
|
458
|
+
const result = { value: "" };
|
|
459
|
+
copyAttr(el, result, "id");
|
|
460
|
+
copyAttr(el, result, "value");
|
|
461
|
+
copyBoolAttr(el, result, "fixed");
|
|
462
|
+
parseAnnotationChild(el, result);
|
|
463
|
+
return result;
|
|
464
|
+
}
|
|
465
|
+
function parsePattern(el) {
|
|
466
|
+
const result = { value: "" };
|
|
467
|
+
copyAttr(el, result, "id");
|
|
468
|
+
copyAttr(el, result, "value");
|
|
469
|
+
parseAnnotationChild(el, result);
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
function parseComplexContent(el) {
|
|
473
|
+
const result = {};
|
|
474
|
+
copyAttr(el, result, "id");
|
|
475
|
+
copyBoolAttr(el, result, "mixed");
|
|
476
|
+
parseAnnotationChild(el, result);
|
|
477
|
+
for (const child of getAllChildElements(el)) {
|
|
478
|
+
const name = getLocalName(child);
|
|
479
|
+
if (name === "restriction") result.restriction = parseComplexContentRestriction(child);
|
|
480
|
+
else if (name === "extension") result.extension = parseComplexContentExtension(child);
|
|
481
|
+
}
|
|
482
|
+
return result;
|
|
483
|
+
}
|
|
484
|
+
function parseSimpleContent(el) {
|
|
485
|
+
const result = {};
|
|
486
|
+
copyAttr(el, result, "id");
|
|
487
|
+
parseAnnotationChild(el, result);
|
|
488
|
+
for (const child of getAllChildElements(el)) {
|
|
489
|
+
const name = getLocalName(child);
|
|
490
|
+
if (name === "restriction") result.restriction = parseSimpleContentRestriction(child);
|
|
491
|
+
else if (name === "extension") result.extension = parseSimpleContentExtension(child);
|
|
492
|
+
}
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
function parseComplexContentRestriction(el) {
|
|
496
|
+
const result = { base: "" };
|
|
497
|
+
copyAttr(el, result, "id");
|
|
498
|
+
copyAttr(el, result, "base");
|
|
499
|
+
parseAnnotationChild(el, result);
|
|
500
|
+
parseComplexTypeContent(el, result);
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
function parseComplexContentExtension(el) {
|
|
504
|
+
const result = { base: "" };
|
|
505
|
+
copyAttr(el, result, "id");
|
|
506
|
+
copyAttr(el, result, "base");
|
|
507
|
+
parseAnnotationChild(el, result);
|
|
508
|
+
parseComplexTypeContent(el, result);
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
511
|
+
function parseSimpleContentRestriction(el) {
|
|
512
|
+
const result = { base: "" };
|
|
513
|
+
copyAttr(el, result, "id");
|
|
514
|
+
copyAttr(el, result, "base");
|
|
515
|
+
parseAnnotationChild(el, result);
|
|
516
|
+
for (const child of getAllChildElements(el)) {
|
|
517
|
+
const name = getLocalName(child);
|
|
518
|
+
switch (name) {
|
|
519
|
+
case "simpleType":
|
|
520
|
+
result.simpleType = parseLocalSimpleType(child);
|
|
521
|
+
break;
|
|
522
|
+
case "minExclusive":
|
|
523
|
+
case "minInclusive":
|
|
524
|
+
case "maxExclusive":
|
|
525
|
+
case "maxInclusive":
|
|
526
|
+
case "totalDigits":
|
|
527
|
+
case "fractionDigits":
|
|
528
|
+
case "length":
|
|
529
|
+
case "minLength":
|
|
530
|
+
case "maxLength":
|
|
531
|
+
case "enumeration":
|
|
532
|
+
case "whiteSpace":
|
|
533
|
+
case "explicitTimezone":
|
|
534
|
+
pushTo(result, name, parseFacet(child));
|
|
535
|
+
break;
|
|
536
|
+
case "pattern":
|
|
537
|
+
pushTo(result, "pattern", parsePattern(child));
|
|
538
|
+
break;
|
|
539
|
+
case "assertion":
|
|
540
|
+
pushTo(result, "assertion", parseAssertion(child));
|
|
541
|
+
break;
|
|
542
|
+
case "attribute":
|
|
543
|
+
pushTo(result, "attribute", parseLocalAttribute(child));
|
|
544
|
+
break;
|
|
545
|
+
case "attributeGroup":
|
|
546
|
+
pushTo(result, "attributeGroup", parseAttributeGroupRef(child));
|
|
547
|
+
break;
|
|
548
|
+
case "anyAttribute":
|
|
549
|
+
result.anyAttribute = parseAnyAttribute(child);
|
|
550
|
+
break;
|
|
551
|
+
case "assert": pushTo(result, "assert", parseAssertion(child));
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return result;
|
|
555
|
+
}
|
|
556
|
+
function parseSimpleContentExtension(el) {
|
|
557
|
+
const result = { base: "" };
|
|
558
|
+
copyAttr(el, result, "id");
|
|
559
|
+
copyAttr(el, result, "base");
|
|
560
|
+
parseAnnotationChild(el, result);
|
|
561
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
562
|
+
case "attribute":
|
|
563
|
+
pushTo(result, "attribute", parseLocalAttribute(child));
|
|
564
|
+
break;
|
|
565
|
+
case "attributeGroup":
|
|
566
|
+
pushTo(result, "attributeGroup", parseAttributeGroupRef(child));
|
|
567
|
+
break;
|
|
568
|
+
case "anyAttribute":
|
|
569
|
+
result.anyAttribute = parseAnyAttribute(child);
|
|
570
|
+
break;
|
|
571
|
+
case "assert": pushTo(result, "assert", parseAssertion(child));
|
|
572
|
+
}
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
function parseExplicitGroup(el) {
|
|
576
|
+
const result = {};
|
|
577
|
+
copyAttr(el, result, "id");
|
|
578
|
+
copyAttr(el, result, "minOccurs");
|
|
579
|
+
copyAttr(el, result, "maxOccurs");
|
|
580
|
+
parseAnnotationChild(el, result);
|
|
581
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
582
|
+
case "element":
|
|
583
|
+
pushTo(result, "element", parseLocalElement(child));
|
|
584
|
+
break;
|
|
585
|
+
case "group":
|
|
586
|
+
pushTo(result, "group", parseGroupRef(child));
|
|
587
|
+
break;
|
|
588
|
+
case "choice":
|
|
589
|
+
pushTo(result, "choice", parseExplicitGroup(child));
|
|
590
|
+
break;
|
|
591
|
+
case "sequence":
|
|
592
|
+
pushTo(result, "sequence", parseExplicitGroup(child));
|
|
593
|
+
break;
|
|
594
|
+
case "any": pushTo(result, "any", parseAny(child));
|
|
595
|
+
}
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
598
|
+
function parseAll(el) {
|
|
599
|
+
const result = {};
|
|
600
|
+
copyAttr(el, result, "id");
|
|
601
|
+
copyAttr(el, result, "minOccurs");
|
|
602
|
+
copyAttr(el, result, "maxOccurs");
|
|
603
|
+
parseAnnotationChild(el, result);
|
|
604
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
605
|
+
case "element":
|
|
606
|
+
pushTo(result, "element", parseLocalElement(child));
|
|
607
|
+
break;
|
|
608
|
+
case "any":
|
|
609
|
+
pushTo(result, "any", parseAny(child));
|
|
610
|
+
break;
|
|
611
|
+
case "group": pushTo(result, "group", parseGroupRef(child));
|
|
612
|
+
}
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
function parseNamedGroup(el) {
|
|
616
|
+
const result = { name: "" };
|
|
617
|
+
copyAttr(el, result, "id");
|
|
618
|
+
copyAttr(el, result, "name");
|
|
619
|
+
parseAnnotationChild(el, result);
|
|
620
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
621
|
+
case "all":
|
|
622
|
+
result.all = parseAll(child);
|
|
623
|
+
break;
|
|
624
|
+
case "choice":
|
|
625
|
+
result.choice = parseExplicitGroup(child);
|
|
626
|
+
break;
|
|
627
|
+
case "sequence": result.sequence = parseExplicitGroup(child);
|
|
628
|
+
}
|
|
629
|
+
return result;
|
|
630
|
+
}
|
|
631
|
+
function parseGroupRef(el) {
|
|
632
|
+
const result = { ref: "" };
|
|
633
|
+
copyAttr(el, result, "id");
|
|
634
|
+
copyAttr(el, result, "ref");
|
|
635
|
+
copyAttr(el, result, "minOccurs");
|
|
636
|
+
copyAttr(el, result, "maxOccurs");
|
|
637
|
+
parseAnnotationChild(el, result);
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
function parseNamedAttributeGroup(el) {
|
|
641
|
+
const result = { name: "" };
|
|
642
|
+
copyAttr(el, result, "id");
|
|
643
|
+
copyAttr(el, result, "name");
|
|
644
|
+
parseAnnotationChild(el, result);
|
|
645
|
+
for (const child of getAllChildElements(el)) switch (getLocalName(child)) {
|
|
646
|
+
case "attribute":
|
|
647
|
+
pushTo(result, "attribute", parseLocalAttribute(child));
|
|
648
|
+
break;
|
|
649
|
+
case "attributeGroup":
|
|
650
|
+
pushTo(result, "attributeGroup", parseAttributeGroupRef(child));
|
|
651
|
+
break;
|
|
652
|
+
case "anyAttribute": result.anyAttribute = parseAnyAttribute(child);
|
|
653
|
+
}
|
|
654
|
+
return result;
|
|
655
|
+
}
|
|
656
|
+
function parseAttributeGroupRef(el) {
|
|
657
|
+
const result = { ref: "" };
|
|
658
|
+
copyAttr(el, result, "id");
|
|
659
|
+
copyAttr(el, result, "ref");
|
|
660
|
+
parseAnnotationChild(el, result);
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
function parseAny(el) {
|
|
664
|
+
const result = {};
|
|
665
|
+
copyAttr(el, result, "id");
|
|
666
|
+
copyAttr(el, result, "minOccurs");
|
|
667
|
+
copyAttr(el, result, "maxOccurs");
|
|
668
|
+
copyAttr(el, result, "namespace");
|
|
669
|
+
copyAttr(el, result, "processContents");
|
|
670
|
+
copyAttr(el, result, "notNamespace");
|
|
671
|
+
copyAttr(el, result, "notQName");
|
|
672
|
+
parseAnnotationChild(el, result);
|
|
673
|
+
return result;
|
|
674
|
+
}
|
|
675
|
+
function parseAnyAttribute(el) {
|
|
676
|
+
const result = {};
|
|
677
|
+
copyAttr(el, result, "id");
|
|
678
|
+
copyAttr(el, result, "namespace");
|
|
679
|
+
copyAttr(el, result, "processContents");
|
|
680
|
+
copyAttr(el, result, "notNamespace");
|
|
681
|
+
copyAttr(el, result, "notQName");
|
|
682
|
+
parseAnnotationChild(el, result);
|
|
683
|
+
return result;
|
|
684
|
+
}
|
|
685
|
+
function parseUnique(el) {
|
|
686
|
+
const result = { name: "" };
|
|
687
|
+
copyAttr(el, result, "id");
|
|
688
|
+
copyAttr(el, result, "name");
|
|
689
|
+
copyAttr(el, result, "ref");
|
|
690
|
+
parseAnnotationChild(el, result);
|
|
691
|
+
for (const child of getAllChildElements(el)) {
|
|
692
|
+
const name = getLocalName(child);
|
|
693
|
+
if (name === "selector") result.selector = parseSelector(child);
|
|
694
|
+
else if (name === "field") pushTo(result, "field", parseField(child));
|
|
695
|
+
}
|
|
696
|
+
return result;
|
|
697
|
+
}
|
|
698
|
+
function parseKey(el) {
|
|
699
|
+
const result = { name: "" };
|
|
700
|
+
copyAttr(el, result, "id");
|
|
701
|
+
copyAttr(el, result, "name");
|
|
702
|
+
copyAttr(el, result, "ref");
|
|
703
|
+
parseAnnotationChild(el, result);
|
|
704
|
+
for (const child of getAllChildElements(el)) {
|
|
705
|
+
const name = getLocalName(child);
|
|
706
|
+
if (name === "selector") result.selector = parseSelector(child);
|
|
707
|
+
else if (name === "field") pushTo(result, "field", parseField(child));
|
|
708
|
+
}
|
|
709
|
+
return result;
|
|
710
|
+
}
|
|
711
|
+
function parseKeyref(el) {
|
|
712
|
+
const result = {
|
|
713
|
+
name: "",
|
|
714
|
+
refer: ""
|
|
715
|
+
};
|
|
716
|
+
copyAttr(el, result, "id");
|
|
717
|
+
copyAttr(el, result, "name");
|
|
718
|
+
copyAttr(el, result, "ref");
|
|
719
|
+
copyAttr(el, result, "refer");
|
|
720
|
+
parseAnnotationChild(el, result);
|
|
721
|
+
for (const child of getAllChildElements(el)) {
|
|
722
|
+
const name = getLocalName(child);
|
|
723
|
+
if (name === "selector") result.selector = parseSelector(child);
|
|
724
|
+
else if (name === "field") pushTo(result, "field", parseField(child));
|
|
725
|
+
}
|
|
726
|
+
return result;
|
|
727
|
+
}
|
|
728
|
+
function parseSelector(el) {
|
|
729
|
+
const result = { xpath: "" };
|
|
730
|
+
copyAttr(el, result, "id");
|
|
731
|
+
copyAttr(el, result, "xpath");
|
|
732
|
+
copyAttr(el, result, "xpathDefaultNamespace");
|
|
733
|
+
parseAnnotationChild(el, result);
|
|
734
|
+
return result;
|
|
735
|
+
}
|
|
736
|
+
function parseField(el) {
|
|
737
|
+
const result = { xpath: "" };
|
|
738
|
+
copyAttr(el, result, "id");
|
|
739
|
+
copyAttr(el, result, "xpath");
|
|
740
|
+
copyAttr(el, result, "xpathDefaultNamespace");
|
|
741
|
+
parseAnnotationChild(el, result);
|
|
742
|
+
return result;
|
|
743
|
+
}
|
|
744
|
+
function parseOpenContent(el) {
|
|
745
|
+
const result = {};
|
|
746
|
+
copyAttr(el, result, "id");
|
|
747
|
+
copyAttr(el, result, "mode");
|
|
748
|
+
parseAnnotationChild(el, result);
|
|
749
|
+
for (const child of getAllChildElements(el)) if (getLocalName(child) === "any") result.any = parseAny(child);
|
|
750
|
+
return result;
|
|
751
|
+
}
|
|
752
|
+
function parseDefaultOpenContent(el) {
|
|
753
|
+
const result = { any: {} };
|
|
754
|
+
copyAttr(el, result, "id");
|
|
755
|
+
copyBoolAttr(el, result, "appliesToEmpty");
|
|
756
|
+
copyAttr(el, result, "mode");
|
|
757
|
+
parseAnnotationChild(el, result);
|
|
758
|
+
for (const child of getAllChildElements(el)) if (getLocalName(child) === "any") result.any = parseAny(child);
|
|
759
|
+
return result;
|
|
760
|
+
}
|
|
761
|
+
function parseAssertion(el) {
|
|
762
|
+
const result = {};
|
|
763
|
+
copyAttr(el, result, "id");
|
|
764
|
+
copyAttr(el, result, "test");
|
|
765
|
+
copyAttr(el, result, "xpathDefaultNamespace");
|
|
766
|
+
parseAnnotationChild(el, result);
|
|
767
|
+
return result;
|
|
768
|
+
}
|
|
769
|
+
function parseAlternative(el) {
|
|
770
|
+
const result = {};
|
|
771
|
+
copyAttr(el, result, "id");
|
|
772
|
+
copyAttr(el, result, "test");
|
|
773
|
+
copyAttr(el, result, "type");
|
|
774
|
+
copyAttr(el, result, "xpathDefaultNamespace");
|
|
775
|
+
parseAnnotationChild(el, result);
|
|
776
|
+
for (const child of getAllChildElements(el)) {
|
|
777
|
+
const name = getLocalName(child);
|
|
778
|
+
if (name === "simpleType") result.simpleType = parseLocalSimpleType(child);
|
|
779
|
+
else if (name === "complexType") result.complexType = parseLocalComplexType(child);
|
|
780
|
+
}
|
|
781
|
+
return result;
|
|
782
|
+
}
|
|
783
|
+
function parseNotation(el) {
|
|
784
|
+
const result = {
|
|
785
|
+
name: "",
|
|
786
|
+
public: ""
|
|
787
|
+
};
|
|
788
|
+
copyAttr(el, result, "id");
|
|
789
|
+
copyAttr(el, result, "name");
|
|
790
|
+
copyAttr(el, result, "public");
|
|
791
|
+
copyAttr(el, result, "system");
|
|
792
|
+
parseAnnotationChild(el, result);
|
|
793
|
+
return result;
|
|
794
|
+
}
|
|
795
|
+
function copyAttr(el, target, name) {
|
|
796
|
+
if (el.hasAttribute(name)) {
|
|
797
|
+
const value = el.getAttribute(name);
|
|
798
|
+
if (value !== null) target[name] = value;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function copyBoolAttr(el, target, name) {
|
|
802
|
+
if (el.hasAttribute(name)) {
|
|
803
|
+
const value = el.getAttribute(name);
|
|
804
|
+
if (value !== null) target[name] = value === "true";
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
function pushTo(target, name, value) {
|
|
808
|
+
if (!target[name]) target[name] = [];
|
|
809
|
+
target[name].push(value);
|
|
810
|
+
}
|
|
811
|
+
function parseAnnotationChild(el, result) {
|
|
812
|
+
for (const child of getAllChildElements(el)) if (getLocalName(child) === "annotation") {
|
|
813
|
+
result.annotation = parseAnnotation(child);
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Extract xmlns declarations from an element's attributes.
|
|
819
|
+
* Returns undefined if no xmlns declarations found.
|
|
820
|
+
*
|
|
821
|
+
* @example
|
|
822
|
+
* For <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://example.com">
|
|
823
|
+
* Returns: { xs: "http://www.w3.org/2001/XMLSchema", tns: "http://example.com" }
|
|
824
|
+
*
|
|
825
|
+
* For <xs:element xmlns="http://default.com">
|
|
826
|
+
* Returns: { "": "http://default.com" }
|
|
827
|
+
*/
|
|
828
|
+
function extractXmlns(el) {
|
|
829
|
+
const xmlns = {};
|
|
830
|
+
let hasXmlns = false;
|
|
831
|
+
for (let i = 0; i < el.attributes.length; i++) {
|
|
832
|
+
const attr = el.attributes[i];
|
|
833
|
+
const name = attr.name;
|
|
834
|
+
const value = attr.value;
|
|
835
|
+
if (name === "xmlns") {
|
|
836
|
+
xmlns[""] = value;
|
|
837
|
+
hasXmlns = true;
|
|
838
|
+
} else if (name.startsWith("xmlns:")) {
|
|
839
|
+
const prefix = name.substring(6);
|
|
840
|
+
xmlns[prefix] = value;
|
|
841
|
+
hasXmlns = true;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return hasXmlns ? xmlns : void 0;
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Copy xmlns declarations to target if present
|
|
848
|
+
*/
|
|
849
|
+
function copyXmlns(el, target) {
|
|
850
|
+
const xmlns = extractXmlns(el);
|
|
851
|
+
if (xmlns) target.$xmlns = xmlns;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
//#endregion
|
|
855
|
+
//#region src/xsd/traverser.ts
|
|
856
|
+
/**
|
|
857
|
+
* Base class for OO schema traversal using real W3C XSD types.
|
|
858
|
+
*
|
|
859
|
+
* Subclass and override the `on*` methods you need.
|
|
860
|
+
* Context is available via `this` - no parameter passing needed.
|
|
861
|
+
*/
|
|
862
|
+
var SchemaTraverser = class {
|
|
863
|
+
/** Root schema being traversed */
|
|
864
|
+
rootSchema;
|
|
865
|
+
/** Current schema being visited */
|
|
866
|
+
currentSchema;
|
|
867
|
+
/** Source of current node (direct, redefine, override, include, import) */
|
|
868
|
+
source = "direct";
|
|
869
|
+
/** Depth in schema hierarchy (0 = root) */
|
|
870
|
+
depth = 0;
|
|
871
|
+
/** Traversal options */
|
|
872
|
+
options = {
|
|
873
|
+
includeImports: true,
|
|
874
|
+
includeIncludes: true,
|
|
875
|
+
maxDepth: Infinity
|
|
876
|
+
};
|
|
877
|
+
/** Visited schemas (prevents infinite loops) */
|
|
878
|
+
visited = /* @__PURE__ */ new Set();
|
|
879
|
+
/**
|
|
880
|
+
* Traverse a schema and all its imports/includes.
|
|
881
|
+
* Override `on*` methods to handle specific node types.
|
|
882
|
+
*/
|
|
883
|
+
traverse(schema, options = {}) {
|
|
884
|
+
this.rootSchema = schema;
|
|
885
|
+
this.options = {
|
|
886
|
+
...this.options,
|
|
887
|
+
...options
|
|
888
|
+
};
|
|
889
|
+
this.visited.clear();
|
|
890
|
+
this.traverseSchema(schema, "direct", 0);
|
|
891
|
+
return this;
|
|
892
|
+
}
|
|
893
|
+
/** Called when entering a schema */
|
|
894
|
+
onEnterSchema(_schema) {}
|
|
895
|
+
/** Called when leaving a schema */
|
|
896
|
+
onLeaveSchema(_schema) {}
|
|
897
|
+
/** Called for each top-level complexType */
|
|
898
|
+
onComplexType(_ct) {}
|
|
899
|
+
/** Called for each top-level simpleType */
|
|
900
|
+
onSimpleType(_st) {}
|
|
901
|
+
/** Called for each top-level element */
|
|
902
|
+
onElement(_element) {}
|
|
903
|
+
/** Called for each top-level attribute */
|
|
904
|
+
onAttribute(_attr) {}
|
|
905
|
+
/** Called for each named group */
|
|
906
|
+
onGroup(_group) {}
|
|
907
|
+
/** Called for each named attributeGroup */
|
|
908
|
+
onAttributeGroup(_group) {}
|
|
909
|
+
/** Called for each redefine block */
|
|
910
|
+
onRedefine(_redefine) {}
|
|
911
|
+
/** Called for each override block */
|
|
912
|
+
onOverride(_override) {}
|
|
913
|
+
traverseSchema(schema, source, depth) {
|
|
914
|
+
if (this.visited.has(schema)) return;
|
|
915
|
+
if (depth > this.options.maxDepth) return;
|
|
916
|
+
this.visited.add(schema);
|
|
917
|
+
this.currentSchema = schema;
|
|
918
|
+
this.source = source;
|
|
919
|
+
this.depth = depth;
|
|
920
|
+
this.onEnterSchema(schema);
|
|
921
|
+
this.traverseSchemaChildren(schema);
|
|
922
|
+
this.traverseRedefines(schema);
|
|
923
|
+
this.traverseOverrides(schema);
|
|
924
|
+
this.traverseIncludes(schema, depth);
|
|
925
|
+
this.traverseImports(schema, depth);
|
|
926
|
+
this.onLeaveSchema(schema);
|
|
927
|
+
}
|
|
928
|
+
traverseSchemaChildren(schema) {
|
|
929
|
+
if (schema.complexType) for (const ct of schema.complexType) this.onComplexType(ct);
|
|
930
|
+
if (schema.simpleType) for (const st of schema.simpleType) this.onSimpleType(st);
|
|
931
|
+
if (schema.element) for (const element of schema.element) this.onElement(element);
|
|
932
|
+
if (schema.attribute) for (const attr of schema.attribute) this.onAttribute(attr);
|
|
933
|
+
if (schema.group) for (const group of schema.group) this.onGroup(group);
|
|
934
|
+
if (schema.attributeGroup) for (const group of schema.attributeGroup) this.onAttributeGroup(group);
|
|
935
|
+
}
|
|
936
|
+
traverseRedefines(schema) {
|
|
937
|
+
if (!schema.redefine) return;
|
|
938
|
+
const prevSource = this.source;
|
|
939
|
+
this.source = "redefine";
|
|
940
|
+
for (const redefine of schema.redefine) {
|
|
941
|
+
this.onRedefine(redefine);
|
|
942
|
+
if (redefine.complexType) for (const ct of redefine.complexType) this.onComplexType(ct);
|
|
943
|
+
if (redefine.simpleType) for (const st of redefine.simpleType) this.onSimpleType(st);
|
|
944
|
+
if (redefine.group) for (const group of redefine.group) this.onGroup(group);
|
|
945
|
+
if (redefine.attributeGroup) for (const group of redefine.attributeGroup) this.onAttributeGroup(group);
|
|
946
|
+
}
|
|
947
|
+
this.source = prevSource;
|
|
948
|
+
}
|
|
949
|
+
traverseOverrides(schema) {
|
|
950
|
+
if (!schema.override) return;
|
|
951
|
+
const prevSource = this.source;
|
|
952
|
+
this.source = "override";
|
|
953
|
+
for (const override of schema.override) {
|
|
954
|
+
this.onOverride(override);
|
|
955
|
+
if (override.complexType) for (const ct of override.complexType) this.onComplexType(ct);
|
|
956
|
+
if (override.simpleType) for (const st of override.simpleType) this.onSimpleType(st);
|
|
957
|
+
if (override.element) for (const element of override.element) this.onElement(element);
|
|
958
|
+
if (override.attribute) for (const attr of override.attribute) this.onAttribute(attr);
|
|
959
|
+
if (override.group) for (const group of override.group) this.onGroup(group);
|
|
960
|
+
if (override.attributeGroup) for (const group of override.attributeGroup) this.onAttributeGroup(group);
|
|
961
|
+
}
|
|
962
|
+
this.source = prevSource;
|
|
963
|
+
}
|
|
964
|
+
traverseIncludes(schema, depth) {
|
|
965
|
+
if (!this.options.includeIncludes || !schema.$includes) return;
|
|
966
|
+
for (const included of schema.$includes) this.traverseSchema(included, "include", depth + 1);
|
|
967
|
+
}
|
|
968
|
+
traverseImports(schema, depth) {
|
|
969
|
+
if (!this.options.includeImports || !schema.$imports) return;
|
|
970
|
+
for (const imported of schema.$imports) this.traverseSchema(imported, "import", depth + 1);
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
/**
|
|
974
|
+
* Resolves a schema hierarchy into a flat structure.
|
|
975
|
+
* All types are collected into Maps for O(1) lookup.
|
|
976
|
+
*
|
|
977
|
+
* Precedence rules:
|
|
978
|
+
* - redefine/override types take precedence over original
|
|
979
|
+
* - root schema types take precedence over imported/included
|
|
980
|
+
*/
|
|
981
|
+
var SchemaResolver = class extends SchemaTraverser {
|
|
982
|
+
complexTypes = /* @__PURE__ */ new Map();
|
|
983
|
+
simpleTypes = /* @__PURE__ */ new Map();
|
|
984
|
+
elements = /* @__PURE__ */ new Map();
|
|
985
|
+
attributes = /* @__PURE__ */ new Map();
|
|
986
|
+
groups = /* @__PURE__ */ new Map();
|
|
987
|
+
attributeGroups = /* @__PURE__ */ new Map();
|
|
988
|
+
xmlns = /* @__PURE__ */ new Map();
|
|
989
|
+
substitutionGroups = /* @__PURE__ */ new Map();
|
|
990
|
+
/** Get the resolved schema structure */
|
|
991
|
+
getResolved() {
|
|
992
|
+
return {
|
|
993
|
+
complexTypes: this.complexTypes,
|
|
994
|
+
simpleTypes: this.simpleTypes,
|
|
995
|
+
elements: this.elements,
|
|
996
|
+
attributes: this.attributes,
|
|
997
|
+
groups: this.groups,
|
|
998
|
+
attributeGroups: this.attributeGroups,
|
|
999
|
+
xmlns: this.xmlns,
|
|
1000
|
+
substitutionGroups: this.substitutionGroups
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
onEnterSchema(schema) {
|
|
1004
|
+
if (schema.$xmlns) {
|
|
1005
|
+
for (const [prefix, uri] of Object.entries(schema.$xmlns)) if (!this.xmlns.has(prefix)) this.xmlns.set(prefix, uri);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
onComplexType(ct) {
|
|
1009
|
+
if (!this.complexTypes.has(ct.name) || this.source === "redefine" || this.source === "override" || this.depth === 0 && this.source === "direct") this.complexTypes.set(ct.name, {
|
|
1010
|
+
ct,
|
|
1011
|
+
schema: this.currentSchema
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
onSimpleType(st) {
|
|
1015
|
+
if (!this.simpleTypes.has(st.name) || this.source === "redefine" || this.source === "override" || this.depth === 0 && this.source === "direct") this.simpleTypes.set(st.name, {
|
|
1016
|
+
st,
|
|
1017
|
+
schema: this.currentSchema
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
onElement(element) {
|
|
1021
|
+
if (!this.elements.has(element.name) || this.source === "override" || this.depth === 0 && this.source === "direct") this.elements.set(element.name, {
|
|
1022
|
+
element,
|
|
1023
|
+
schema: this.currentSchema
|
|
1024
|
+
});
|
|
1025
|
+
if (element.substitutionGroup) {
|
|
1026
|
+
const abstractName = stripNsPrefix(element.substitutionGroup);
|
|
1027
|
+
const existing = this.substitutionGroups.get(abstractName) ?? [];
|
|
1028
|
+
existing.push(element);
|
|
1029
|
+
this.substitutionGroups.set(abstractName, existing);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
onAttribute(attr) {
|
|
1033
|
+
if (!this.attributes.has(attr.name) || this.source === "override" || this.depth === 0 && this.source === "direct") this.attributes.set(attr.name, {
|
|
1034
|
+
attr,
|
|
1035
|
+
schema: this.currentSchema
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
onGroup(group) {
|
|
1039
|
+
if (!this.groups.has(group.name) || this.source === "redefine" || this.source === "override" || this.depth === 0 && this.source === "direct") this.groups.set(group.name, {
|
|
1040
|
+
group,
|
|
1041
|
+
schema: this.currentSchema
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
onAttributeGroup(group) {
|
|
1045
|
+
if (!this.attributeGroups.has(group.name) || this.source === "redefine" || this.source === "override" || this.depth === 0 && this.source === "direct") this.attributeGroups.set(group.name, {
|
|
1046
|
+
group,
|
|
1047
|
+
schema: this.currentSchema
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
/**
|
|
1052
|
+
* Strip namespace prefix from a QName (e.g., "xs:string" -> "string")
|
|
1053
|
+
*/
|
|
1054
|
+
function stripNsPrefix(qname) {
|
|
1055
|
+
const colonIndex = qname.indexOf(":");
|
|
1056
|
+
return colonIndex >= 0 ? qname.slice(colonIndex + 1) : qname;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Resolve a schema hierarchy into a flat structure.
|
|
1060
|
+
* Convenience function that creates a SchemaResolver and returns the result.
|
|
1061
|
+
*/
|
|
1062
|
+
function resolveSchemaTypes(schema, options = {}) {
|
|
1063
|
+
const resolver = new SchemaResolver();
|
|
1064
|
+
resolver.traverse(schema, options);
|
|
1065
|
+
return resolver.getResolved();
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
//#endregion
|
|
1069
|
+
//#region src/xsd/resolve.ts
|
|
1070
|
+
/**
|
|
1071
|
+
* Collects all schema components into Maps for resolution.
|
|
1072
|
+
*
|
|
1073
|
+
* IMPORTANT: Elements are only collected from schemas with the SAME targetNamespace
|
|
1074
|
+
* as the root schema. This follows XSD semantics where xs:import brings in types
|
|
1075
|
+
* from a different namespace but doesn't merge elements into the current namespace.
|
|
1076
|
+
*/
|
|
1077
|
+
var SchemaCollector = class extends SchemaTraverser {
|
|
1078
|
+
complexTypes = /* @__PURE__ */ new Map();
|
|
1079
|
+
simpleTypes = /* @__PURE__ */ new Map();
|
|
1080
|
+
elements = /* @__PURE__ */ new Map();
|
|
1081
|
+
groups = /* @__PURE__ */ new Map();
|
|
1082
|
+
attributeGroups = /* @__PURE__ */ new Map();
|
|
1083
|
+
substitutionGroups = /* @__PURE__ */ new Map();
|
|
1084
|
+
xmlns = /* @__PURE__ */ new Map();
|
|
1085
|
+
onEnterSchema(schema) {
|
|
1086
|
+
if (schema.$xmlns) {
|
|
1087
|
+
for (const [prefix, uri] of Object.entries(schema.$xmlns)) if (!this.xmlns.has(prefix)) this.xmlns.set(prefix, uri);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
onComplexType(ct) {
|
|
1091
|
+
if (!this.complexTypes.has(ct.name)) this.complexTypes.set(ct.name, ct);
|
|
1092
|
+
}
|
|
1093
|
+
onSimpleType(st) {
|
|
1094
|
+
if (!this.simpleTypes.has(st.name)) this.simpleTypes.set(st.name, st);
|
|
1095
|
+
}
|
|
1096
|
+
onElement(element) {
|
|
1097
|
+
const rootNs = this.rootSchema.targetNamespace;
|
|
1098
|
+
const currentNs = this.currentSchema.targetNamespace;
|
|
1099
|
+
const sameOrNoNamespace = currentNs === rootNs || !currentNs;
|
|
1100
|
+
const hasSubstitutionGroup = !!element.substitutionGroup;
|
|
1101
|
+
const isAbstract = !!element.abstract;
|
|
1102
|
+
if ((sameOrNoNamespace || hasSubstitutionGroup || isAbstract) && !this.elements.has(element.name)) this.elements.set(element.name, element);
|
|
1103
|
+
if (element.substitutionGroup) {
|
|
1104
|
+
const abstractName = stripNsPrefix(element.substitutionGroup);
|
|
1105
|
+
const existing = this.substitutionGroups.get(abstractName) ?? [];
|
|
1106
|
+
existing.push(element);
|
|
1107
|
+
this.substitutionGroups.set(abstractName, existing);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
onGroup(group) {
|
|
1111
|
+
if (!this.groups.has(group.name)) this.groups.set(group.name, group);
|
|
1112
|
+
}
|
|
1113
|
+
onAttributeGroup(group) {
|
|
1114
|
+
if (!this.attributeGroups.has(group.name)) this.attributeGroups.set(group.name, group);
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
/**
|
|
1118
|
+
* Resolve a schema by merging all imports/includes and expanding all references.
|
|
1119
|
+
* Returns a new self-contained schema with no external dependencies.
|
|
1120
|
+
*/
|
|
1121
|
+
function resolveSchema(schema, options = {}) {
|
|
1122
|
+
const { resolveImports = true, resolveIncludes = true, expandExtensions = true, expandSubstitutions = true, filterToRootElements = false, keepImportsRef = false } = options;
|
|
1123
|
+
const collector = new SchemaCollector();
|
|
1124
|
+
collector.traverse(schema, {
|
|
1125
|
+
includeImports: resolveImports,
|
|
1126
|
+
includeIncludes: resolveIncludes
|
|
1127
|
+
});
|
|
1128
|
+
let elements;
|
|
1129
|
+
if (filterToRootElements) {
|
|
1130
|
+
const referencedElements = /* @__PURE__ */ new Set();
|
|
1131
|
+
for (const el of collector.elements.values()) collectElementRefs(el, referencedElements);
|
|
1132
|
+
for (const ct of collector.complexTypes.values()) collectElementRefsFromType(ct, referencedElements);
|
|
1133
|
+
elements = Array.from(collector.elements.values()).filter((el) => {
|
|
1134
|
+
if (el.abstract === true) return false;
|
|
1135
|
+
if (referencedElements.has(el.name)) return false;
|
|
1136
|
+
return true;
|
|
1137
|
+
});
|
|
1138
|
+
} else elements = Array.from(collector.elements.values());
|
|
1139
|
+
let resolvedComplexTypes = Array.from(collector.complexTypes.values());
|
|
1140
|
+
if (expandExtensions) resolvedComplexTypes = resolvedComplexTypes.map((ct) => expandComplexTypeExtension(ct, collector.complexTypes));
|
|
1141
|
+
if (expandSubstitutions) resolvedComplexTypes = resolvedComplexTypes.map((ct) => expandSubstitutionGroupsInType(ct, collector.elements, collector.substitutionGroups));
|
|
1142
|
+
const resolved = {
|
|
1143
|
+
targetNamespace: schema.targetNamespace,
|
|
1144
|
+
elementFormDefault: schema.elementFormDefault,
|
|
1145
|
+
$filename: schema.$filename,
|
|
1146
|
+
$xmlns: collector.xmlns.size > 0 ? Object.fromEntries(collector.xmlns) : schema.$xmlns
|
|
1147
|
+
};
|
|
1148
|
+
if (elements.length > 0) resolved.element = elements;
|
|
1149
|
+
if (resolvedComplexTypes.length > 0) resolved.complexType = resolvedComplexTypes;
|
|
1150
|
+
if (collector.simpleTypes.size > 0) resolved.simpleType = Array.from(collector.simpleTypes.values());
|
|
1151
|
+
if (collector.groups.size > 0) resolved.group = Array.from(collector.groups.values());
|
|
1152
|
+
if (collector.attributeGroups.size > 0) resolved.attributeGroup = Array.from(collector.attributeGroups.values());
|
|
1153
|
+
if (keepImportsRef && schema.$imports) resolved.$imports = schema.$imports;
|
|
1154
|
+
return resolved;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Expand complexContent/extension by merging base type properties.
|
|
1158
|
+
* Uses a visited set to prevent infinite recursion on circular inheritance.
|
|
1159
|
+
*/
|
|
1160
|
+
function expandComplexTypeExtension(ct, allTypes, visited = /* @__PURE__ */ new Set()) {
|
|
1161
|
+
const extension = ct.complexContent?.extension;
|
|
1162
|
+
if (!extension?.base) return ct;
|
|
1163
|
+
if (visited.has(ct.name)) return ct;
|
|
1164
|
+
visited.add(ct.name);
|
|
1165
|
+
const mergedElements = [];
|
|
1166
|
+
const mergedAttributes = [];
|
|
1167
|
+
const baseName = stripNsPrefix(extension.base);
|
|
1168
|
+
const baseType = allTypes.get(baseName);
|
|
1169
|
+
if (baseType && !visited.has(baseName)) {
|
|
1170
|
+
const expandedBase = expandComplexTypeExtension(baseType, allTypes, visited);
|
|
1171
|
+
collectElementsFromType(expandedBase, mergedElements);
|
|
1172
|
+
if (expandedBase.attribute) mergedAttributes.push(...expandedBase.attribute);
|
|
1173
|
+
}
|
|
1174
|
+
collectElementsFromGroup(extension.sequence, mergedElements);
|
|
1175
|
+
collectElementsFromGroup(extension.choice, mergedElements);
|
|
1176
|
+
collectElementsFromGroup(extension.all, mergedElements);
|
|
1177
|
+
if (extension.attribute) mergedAttributes.push(...extension.attribute);
|
|
1178
|
+
const flattened = { name: ct.name };
|
|
1179
|
+
if (mergedElements.length > 0) flattened.all = { element: mergedElements };
|
|
1180
|
+
if (mergedAttributes.length > 0) flattened.attribute = mergedAttributes;
|
|
1181
|
+
for (const [key, value] of Object.entries(ct)) if (![
|
|
1182
|
+
"name",
|
|
1183
|
+
"complexContent",
|
|
1184
|
+
"all",
|
|
1185
|
+
"sequence",
|
|
1186
|
+
"choice",
|
|
1187
|
+
"attribute"
|
|
1188
|
+
].includes(key)) flattened[key] = value;
|
|
1189
|
+
return flattened;
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Collect elements from a complex type's content model.
|
|
1193
|
+
*/
|
|
1194
|
+
function collectElementsFromType(ct, elements) {
|
|
1195
|
+
collectElementsFromGroup(ct.sequence, elements);
|
|
1196
|
+
collectElementsFromGroup(ct.choice, elements);
|
|
1197
|
+
collectElementsFromGroup(ct.all, elements);
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Collect elements from a group (sequence/choice/all).
|
|
1201
|
+
*/
|
|
1202
|
+
function collectElementsFromGroup(group, elements) {
|
|
1203
|
+
if (!group?.element) return;
|
|
1204
|
+
elements.push(...group.element);
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Expand substitution group references in a complex type.
|
|
1208
|
+
* Replaces abstract element refs with concrete substitute elements.
|
|
1209
|
+
*/
|
|
1210
|
+
function expandSubstitutionGroupsInType(ct, allElements, substitutionGroups) {
|
|
1211
|
+
let changed = false;
|
|
1212
|
+
const result = { ...ct };
|
|
1213
|
+
if (ct.sequence) {
|
|
1214
|
+
const expanded = expandSubstitutionGroupsInGroup(ct.sequence, allElements, substitutionGroups);
|
|
1215
|
+
if (expanded !== ct.sequence) {
|
|
1216
|
+
result.sequence = expanded;
|
|
1217
|
+
changed = true;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
if (ct.all) {
|
|
1221
|
+
const expanded = expandSubstitutionGroupsInGroup(ct.all, allElements, substitutionGroups);
|
|
1222
|
+
if (expanded !== ct.all) {
|
|
1223
|
+
result.all = expanded;
|
|
1224
|
+
changed = true;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (ct.choice) {
|
|
1228
|
+
const expanded = expandSubstitutionGroupsInGroup(ct.choice, allElements, substitutionGroups);
|
|
1229
|
+
if (expanded !== ct.choice) {
|
|
1230
|
+
result.choice = expanded;
|
|
1231
|
+
changed = true;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return changed ? result : ct;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Expand substitution groups in a group (sequence/all/choice).
|
|
1238
|
+
*/
|
|
1239
|
+
function expandSubstitutionGroupsInGroup(group, allElements, substitutionGroups) {
|
|
1240
|
+
if (!group.element) return group;
|
|
1241
|
+
const expandedElements = [];
|
|
1242
|
+
let hasChanges = false;
|
|
1243
|
+
for (const el of group.element) {
|
|
1244
|
+
if (el.ref) {
|
|
1245
|
+
const refName = stripNsPrefix(el.ref);
|
|
1246
|
+
if (allElements.get(refName)?.abstract) {
|
|
1247
|
+
const substitutes = substitutionGroups.get(refName);
|
|
1248
|
+
if (substitutes && substitutes.length > 0) {
|
|
1249
|
+
for (const sub of substitutes) expandedElements.push({
|
|
1250
|
+
name: sub.name,
|
|
1251
|
+
type: sub.type,
|
|
1252
|
+
minOccurs: el.minOccurs ?? 0,
|
|
1253
|
+
maxOccurs: el.maxOccurs
|
|
1254
|
+
});
|
|
1255
|
+
hasChanges = true;
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
expandedElements.push(el);
|
|
1261
|
+
}
|
|
1262
|
+
if (!hasChanges) return group;
|
|
1263
|
+
return {
|
|
1264
|
+
...group,
|
|
1265
|
+
element: expandedElements
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Get all substitutes for an abstract element.
|
|
1270
|
+
*/
|
|
1271
|
+
function getSubstitutes(abstractElementName, schema) {
|
|
1272
|
+
const collector = new SchemaCollector();
|
|
1273
|
+
collector.traverse(schema);
|
|
1274
|
+
return collector.substitutionGroups.get(abstractElementName) ?? [];
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Collect element refs from an element's nested structure
|
|
1278
|
+
*/
|
|
1279
|
+
function collectElementRefs(el, refs) {
|
|
1280
|
+
const record = el;
|
|
1281
|
+
if (record.complexType) collectElementRefsFromType(record.complexType, refs);
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Recursively collect element refs from a complex type
|
|
1285
|
+
*/
|
|
1286
|
+
function collectElementRefsFromType(ct, refs) {
|
|
1287
|
+
const record = ct;
|
|
1288
|
+
for (const groupKey of [
|
|
1289
|
+
"sequence",
|
|
1290
|
+
"all",
|
|
1291
|
+
"choice"
|
|
1292
|
+
]) {
|
|
1293
|
+
const group = record[groupKey];
|
|
1294
|
+
if (group?.element) {
|
|
1295
|
+
const elements = group.element;
|
|
1296
|
+
for (const el of elements) {
|
|
1297
|
+
if (el.ref) {
|
|
1298
|
+
const refName = stripNsPrefix(el.ref);
|
|
1299
|
+
refs.add(refName);
|
|
1300
|
+
}
|
|
1301
|
+
if (el.complexType) collectElementRefsFromType(el.complexType, refs);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
if (record.complexContent) {
|
|
1306
|
+
const cc = record.complexContent;
|
|
1307
|
+
if (cc.extension) collectElementRefsFromType(cc.extension, refs);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
//#endregion
|
|
1312
|
+
//#region src/xsd/loader.ts
|
|
1313
|
+
/**
|
|
1314
|
+
* Schema Loader - Load and parse XSD files from disk
|
|
1315
|
+
*
|
|
1316
|
+
* Simple loader that reads XSD files and parses them into Schema objects.
|
|
1317
|
+
* Resolution of imports/includes is handled by the resolver.
|
|
1318
|
+
*/
|
|
1319
|
+
/**
|
|
1320
|
+
* Default XSD loader - reads from filesystem
|
|
1321
|
+
*/
|
|
1322
|
+
function defaultLoader(schemaLocation, basePath) {
|
|
1323
|
+
const fullPath = resolve(basePath, schemaLocation);
|
|
1324
|
+
if (!existsSync(fullPath)) return null;
|
|
1325
|
+
return readFileSync(fullPath, "utf-8");
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Load and parse a single XSD file.
|
|
1329
|
+
*
|
|
1330
|
+
* @param schemaPath - Path to the XSD file
|
|
1331
|
+
* @param options - Loader options
|
|
1332
|
+
* @param options.autoLink - Populate $imports/$includes with loaded schemas (preserves structure)
|
|
1333
|
+
* @param options.autoResolve - Flatten into single schema with all types merged (implies autoLink)
|
|
1334
|
+
* @returns Parsed schema
|
|
1335
|
+
*
|
|
1336
|
+
* @example
|
|
1337
|
+
* ```typescript
|
|
1338
|
+
* // Just parse - no linking
|
|
1339
|
+
* const schema = loadSchema('/path/to/schema.xsd');
|
|
1340
|
+
*
|
|
1341
|
+
* // Link - populate $imports/$includes (preserves multi-schema structure)
|
|
1342
|
+
* const linked = loadSchema('/path/to/schema.xsd', { autoLink: true });
|
|
1343
|
+
* // linked.$imports contains actual Schema objects
|
|
1344
|
+
*
|
|
1345
|
+
* // Resolve - flatten everything into one schema
|
|
1346
|
+
* const resolved = loadSchema('/path/to/schema.xsd', { autoResolve: true });
|
|
1347
|
+
* // resolved has all types merged, no $imports/$includes
|
|
1348
|
+
* ```
|
|
1349
|
+
*/
|
|
1350
|
+
function loadSchema(schemaPath, options = {}) {
|
|
1351
|
+
const { basePath = dirname(schemaPath), loader = defaultLoader, autoLink = false, autoResolve = false, throwOnMissing = false } = options;
|
|
1352
|
+
const content = loader(schemaPath, basePath);
|
|
1353
|
+
if (!content) throw new Error(`Failed to load schema: ${schemaPath}`);
|
|
1354
|
+
let schema = parseXsd(content);
|
|
1355
|
+
Object.assign(schema, { $filename: schemaPath });
|
|
1356
|
+
if (autoLink || autoResolve) linkSchema(schema, {
|
|
1357
|
+
basePath,
|
|
1358
|
+
loader,
|
|
1359
|
+
throwOnMissing
|
|
1360
|
+
});
|
|
1361
|
+
if (autoResolve) schema = resolveSchema(schema);
|
|
1362
|
+
return schema;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Load and parse XSD content from a string.
|
|
1366
|
+
*/
|
|
1367
|
+
function parseSchemaContent(content, filename) {
|
|
1368
|
+
const schema = parseXsd(content);
|
|
1369
|
+
if (filename) Object.assign(schema, { $filename: filename });
|
|
1370
|
+
return schema;
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Create a schema loader function that can be passed to the resolver.
|
|
1374
|
+
* The resolver will call this to load schemas referenced by schemaLocation.
|
|
1375
|
+
*/
|
|
1376
|
+
function createSchemaLoader(basePath, loader = defaultLoader) {
|
|
1377
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1378
|
+
return (schemaLocation) => {
|
|
1379
|
+
const fullPath = resolve(basePath, schemaLocation);
|
|
1380
|
+
const cached = cache.get(fullPath);
|
|
1381
|
+
if (cached) return cached;
|
|
1382
|
+
const content = loader(schemaLocation, basePath);
|
|
1383
|
+
if (!content) return null;
|
|
1384
|
+
const schema = parseXsd(content);
|
|
1385
|
+
Object.assign(schema, { $filename: schemaLocation });
|
|
1386
|
+
cache.set(fullPath, schema);
|
|
1387
|
+
return schema;
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Link a schema by resolving all schemaLocation references.
|
|
1392
|
+
*
|
|
1393
|
+
* Processes:
|
|
1394
|
+
* - xs:import → populates $imports
|
|
1395
|
+
* - xs:include → populates $includes
|
|
1396
|
+
* - xs:redefine → loads base schema, keeps redefinitions in redefine array
|
|
1397
|
+
* - xs:override → loads base schema, keeps overrides in override array
|
|
1398
|
+
*
|
|
1399
|
+
* @param schema - Schema to link (will be mutated with $imports/$includes)
|
|
1400
|
+
* @param options - Link options including basePath for resolving paths
|
|
1401
|
+
* @returns The same schema with $imports and $includes populated
|
|
1402
|
+
*
|
|
1403
|
+
* @example
|
|
1404
|
+
* ```typescript
|
|
1405
|
+
* const schema = parseXsd(xsdContent);
|
|
1406
|
+
* linkSchema(schema, { basePath: '/path/to/xsd/files' });
|
|
1407
|
+
* // schema.$imports and schema.$includes are now populated
|
|
1408
|
+
* ```
|
|
1409
|
+
*/
|
|
1410
|
+
function linkSchema(schema, options) {
|
|
1411
|
+
const { basePath, loader = defaultLoader, throwOnMissing = true } = options;
|
|
1412
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1413
|
+
const inProgress = /* @__PURE__ */ new Set();
|
|
1414
|
+
const loadAndLink = (schemaLocation, currentBasePath) => {
|
|
1415
|
+
const fullPath = resolve(currentBasePath, schemaLocation);
|
|
1416
|
+
const cached = cache.get(fullPath);
|
|
1417
|
+
if (cached) return cached;
|
|
1418
|
+
if (inProgress.has(fullPath)) return cache.get(fullPath) ?? null;
|
|
1419
|
+
const content = loader(schemaLocation, currentBasePath);
|
|
1420
|
+
if (!content) {
|
|
1421
|
+
if (throwOnMissing) throw new Error(`Failed to load schema: ${schemaLocation} (resolved: ${fullPath})`);
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
const loadedSchema = parseXsd(content);
|
|
1425
|
+
Object.assign(loadedSchema, { $filename: schemaLocation });
|
|
1426
|
+
inProgress.add(fullPath);
|
|
1427
|
+
cache.set(fullPath, loadedSchema);
|
|
1428
|
+
const newBasePath = dirname(fullPath);
|
|
1429
|
+
linkSchemaInternal(loadedSchema, newBasePath);
|
|
1430
|
+
inProgress.delete(fullPath);
|
|
1431
|
+
return loadedSchema;
|
|
1432
|
+
};
|
|
1433
|
+
const linkSchemaInternal = (s, currentBasePath) => {
|
|
1434
|
+
const imports = [];
|
|
1435
|
+
const includes = [];
|
|
1436
|
+
if (s.import) {
|
|
1437
|
+
for (const imp of s.import) if (imp.schemaLocation) {
|
|
1438
|
+
const linked = loadAndLink(imp.schemaLocation, currentBasePath);
|
|
1439
|
+
if (linked) imports.push(linked);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (s.include) {
|
|
1443
|
+
for (const inc of s.include) if (inc.schemaLocation) {
|
|
1444
|
+
const linked = loadAndLink(inc.schemaLocation, currentBasePath);
|
|
1445
|
+
if (linked) includes.push(linked);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
if (s.redefine) {
|
|
1449
|
+
for (const redef of s.redefine) if (redef.schemaLocation) {
|
|
1450
|
+
const linked = loadAndLink(redef.schemaLocation, currentBasePath);
|
|
1451
|
+
if (linked) Object.assign(redef, { $schema: linked });
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
if (s.override) {
|
|
1455
|
+
for (const ovr of s.override) if (ovr.schemaLocation) {
|
|
1456
|
+
const linked = loadAndLink(ovr.schemaLocation, currentBasePath);
|
|
1457
|
+
if (linked) Object.assign(ovr, { $schema: linked });
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
if (imports.length > 0) Object.assign(s, { $imports: imports });
|
|
1461
|
+
if (includes.length > 0) Object.assign(s, { $includes: includes });
|
|
1462
|
+
};
|
|
1463
|
+
linkSchemaInternal(schema, basePath);
|
|
1464
|
+
return schema;
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Load and link a schema from a file path.
|
|
1468
|
+
* Convenience function that combines loadSchema + linkSchema.
|
|
1469
|
+
*
|
|
1470
|
+
* @param schemaPath - Path to the XSD file
|
|
1471
|
+
* @param options - Optional loader options
|
|
1472
|
+
* @returns Fully linked schema with $imports and $includes populated
|
|
1473
|
+
*
|
|
1474
|
+
* @example
|
|
1475
|
+
* ```typescript
|
|
1476
|
+
* const schema = loadAndLinkSchema('/path/to/schema.xsd');
|
|
1477
|
+
* // schema.$imports and schema.$includes are populated
|
|
1478
|
+
* // Ready for type inference or resolution
|
|
1479
|
+
* ```
|
|
1480
|
+
*/
|
|
1481
|
+
function loadAndLinkSchema(schemaPath, options = {}) {
|
|
1482
|
+
const basePath = dirname(schemaPath);
|
|
1483
|
+
return linkSchema(loadSchema(schemaPath, {
|
|
1484
|
+
...options,
|
|
1485
|
+
basePath
|
|
1486
|
+
}), {
|
|
1487
|
+
basePath,
|
|
1488
|
+
loader: options.loader
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
//#endregion
|
|
1493
|
+
//#region src/codegen/runner.ts
|
|
1494
|
+
/**
|
|
1495
|
+
* Config Runner
|
|
1496
|
+
*
|
|
1497
|
+
* Executes the generator pipeline based on configuration.
|
|
1498
|
+
*/
|
|
1499
|
+
/**
|
|
1500
|
+
* Run the codegen pipeline
|
|
1501
|
+
*/
|
|
1502
|
+
async function runCodegen(config, options) {
|
|
1503
|
+
const { rootDir, dryRun = false, verbose = false } = options;
|
|
1504
|
+
const result = {
|
|
1505
|
+
files: [],
|
|
1506
|
+
schemas: [],
|
|
1507
|
+
errors: []
|
|
1508
|
+
};
|
|
1509
|
+
const log = verbose ? console.log.bind(console) : () => {};
|
|
1510
|
+
const sources = {};
|
|
1511
|
+
for (const [name, sourceConfig] of Object.entries(config.sources)) {
|
|
1512
|
+
const xsdDir = isAbsolute(sourceConfig.xsdDir) ? sourceConfig.xsdDir : join(rootDir, sourceConfig.xsdDir);
|
|
1513
|
+
let schemas = sourceConfig.schemas;
|
|
1514
|
+
if (sourceConfig.autoLink) schemas = discoverDependentSchemas(sourceConfig.schemas, xsdDir, log);
|
|
1515
|
+
sources[name] = {
|
|
1516
|
+
name,
|
|
1517
|
+
xsdDir,
|
|
1518
|
+
outputDir: isAbsolute(sourceConfig.outputDir) ? sourceConfig.outputDir : join(rootDir, sourceConfig.outputDir),
|
|
1519
|
+
schemas
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
const setupCtx = {
|
|
1523
|
+
sources,
|
|
1524
|
+
rootDir
|
|
1525
|
+
};
|
|
1526
|
+
if (config.clean) {
|
|
1527
|
+
log(`\n🧹 Cleaning output directories...`);
|
|
1528
|
+
for (const source of Object.values(sources)) if (existsSync(source.outputDir)) {
|
|
1529
|
+
rmSync(source.outputDir, {
|
|
1530
|
+
recursive: true,
|
|
1531
|
+
force: true
|
|
1532
|
+
});
|
|
1533
|
+
log(` ✅ Cleaned ${relative(rootDir, source.outputDir)}`);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
if (config.beforeAll) {
|
|
1537
|
+
log(`\n🔧 Running beforeAll hook...`);
|
|
1538
|
+
await config.beforeAll({
|
|
1539
|
+
sources,
|
|
1540
|
+
rootDir
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
for (const generator of config.generators) if (generator.setup) {
|
|
1544
|
+
log(`[${generator.name}] Running setup...`);
|
|
1545
|
+
await generator.setup(setupCtx);
|
|
1546
|
+
}
|
|
1547
|
+
const processedSchemas = /* @__PURE__ */ new Map();
|
|
1548
|
+
const globalAllSchemas = /* @__PURE__ */ new Map();
|
|
1549
|
+
/**
|
|
1550
|
+
* Recursively discover and parse schemas referenced via xs:include or xs:redefine.
|
|
1551
|
+
* This ensures schemas like types/devc.xsd are in globalAllSchemas when devc.xsd references them.
|
|
1552
|
+
*
|
|
1553
|
+
* @param schema - The parsed schema object
|
|
1554
|
+
* @param xsdDir - The root XSD directory (e.g., '.xsd')
|
|
1555
|
+
* @param currentXsdPath - The path of the current XSD file (for resolving relative paths)
|
|
1556
|
+
* @param sourceName - The source name for logging
|
|
1557
|
+
* @param discovered - Set of already discovered schema names
|
|
1558
|
+
*/
|
|
1559
|
+
function discoverReferencedSchemas(schema, xsdDir, currentXsdPath, sourceName, discovered = /* @__PURE__ */ new Set()) {
|
|
1560
|
+
const includes = schema.include;
|
|
1561
|
+
const redefines = schema.redefine;
|
|
1562
|
+
const refs = [...includes ?? [], ...redefines ?? []];
|
|
1563
|
+
for (const ref of refs) {
|
|
1564
|
+
if (!ref.schemaLocation) continue;
|
|
1565
|
+
const schemaNameWithPath = ref.schemaLocation.replace(/\.xsd$/, "");
|
|
1566
|
+
if (discovered.has(schemaNameWithPath) || globalAllSchemas.has(schemaNameWithPath)) continue;
|
|
1567
|
+
discovered.add(schemaNameWithPath);
|
|
1568
|
+
const currentXsdDir = dirname(currentXsdPath);
|
|
1569
|
+
const refXsdPath = join(currentXsdDir, ref.schemaLocation);
|
|
1570
|
+
if (!existsSync(refXsdPath)) {
|
|
1571
|
+
log(` ⚠️ Referenced schema not found: ${ref.schemaLocation} (resolved from ${currentXsdDir})`);
|
|
1572
|
+
continue;
|
|
1573
|
+
}
|
|
1574
|
+
try {
|
|
1575
|
+
const refXsdContent = readFileSync(refXsdPath, "utf-8");
|
|
1576
|
+
const refSchema = parseXsd(refXsdContent);
|
|
1577
|
+
refSchema.$filename = ref.schemaLocation;
|
|
1578
|
+
const refSchemaInfo = {
|
|
1579
|
+
name: schemaNameWithPath,
|
|
1580
|
+
xsdContent: refXsdContent,
|
|
1581
|
+
schema: refSchema,
|
|
1582
|
+
sourceName,
|
|
1583
|
+
xsdPath: refXsdPath
|
|
1584
|
+
};
|
|
1585
|
+
globalAllSchemas.set(schemaNameWithPath, refSchemaInfo);
|
|
1586
|
+
log(` 📎 Auto-discovered: ${schemaNameWithPath}`);
|
|
1587
|
+
discoverReferencedSchemas(refSchema, xsdDir, refXsdPath, sourceName, discovered);
|
|
1588
|
+
} catch (error) {
|
|
1589
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1590
|
+
log(` ⚠️ Failed to parse referenced schema ${ref.schemaLocation}: ${err.message}`);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
for (const [sourceName, source] of Object.entries(sources)) {
|
|
1595
|
+
log(`\n📦 Processing source: ${sourceName}`);
|
|
1596
|
+
const schemaInfos = [];
|
|
1597
|
+
for (const schemaName of source.schemas) {
|
|
1598
|
+
const xsdPath = join(source.xsdDir, `${schemaName}.xsd`);
|
|
1599
|
+
if (!existsSync(xsdPath)) {
|
|
1600
|
+
log(` ⚠️ Skipping ${schemaName} - XSD not found`);
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
try {
|
|
1604
|
+
const xsdContent = readFileSync(xsdPath, "utf-8");
|
|
1605
|
+
const schema = parseXsd(xsdContent);
|
|
1606
|
+
schema.$filename = `${schemaName}.xsd`;
|
|
1607
|
+
const basePath = dirname(xsdPath);
|
|
1608
|
+
linkSchema(schema, {
|
|
1609
|
+
basePath,
|
|
1610
|
+
throwOnMissing: false
|
|
1611
|
+
});
|
|
1612
|
+
const schemaInfo = {
|
|
1613
|
+
name: schemaName,
|
|
1614
|
+
xsdContent,
|
|
1615
|
+
schema,
|
|
1616
|
+
sourceName,
|
|
1617
|
+
xsdPath
|
|
1618
|
+
};
|
|
1619
|
+
schemaInfos.push(schemaInfo);
|
|
1620
|
+
globalAllSchemas.set(schemaName, schemaInfo);
|
|
1621
|
+
result.schemas.push({
|
|
1622
|
+
name: schemaName,
|
|
1623
|
+
source: sourceName
|
|
1624
|
+
});
|
|
1625
|
+
discoverReferencedSchemas(schema, source.xsdDir, xsdPath, sourceName);
|
|
1626
|
+
} catch (error) {
|
|
1627
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1628
|
+
log(` ❌ Failed to parse ${schemaName}: ${err.message}`);
|
|
1629
|
+
result.errors.push({
|
|
1630
|
+
schema: schemaName,
|
|
1631
|
+
source: sourceName,
|
|
1632
|
+
error: err
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
processedSchemas.set(sourceName, schemaInfos);
|
|
1637
|
+
}
|
|
1638
|
+
for (const [sourceName, source] of Object.entries(sources)) {
|
|
1639
|
+
const schemaInfos = processedSchemas.get(sourceName) ?? [];
|
|
1640
|
+
for (const schemaInfo of schemaInfos) {
|
|
1641
|
+
const transformCtx = {
|
|
1642
|
+
schema: schemaInfo,
|
|
1643
|
+
source,
|
|
1644
|
+
allSchemas: globalAllSchemas,
|
|
1645
|
+
allSources: sources,
|
|
1646
|
+
rootDir,
|
|
1647
|
+
resolveImport: createImportResolver(schemaInfo, source, sources, config)
|
|
1648
|
+
};
|
|
1649
|
+
for (const generator of config.generators) if (generator.transform) try {
|
|
1650
|
+
const files = await generator.transform(transformCtx);
|
|
1651
|
+
for (const file of files) {
|
|
1652
|
+
const fullPath = join(source.outputDir, file.path);
|
|
1653
|
+
if (!dryRun) {
|
|
1654
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
1655
|
+
writeFileSync(fullPath, file.content);
|
|
1656
|
+
}
|
|
1657
|
+
result.files.push({
|
|
1658
|
+
path: fullPath,
|
|
1659
|
+
source: sourceName
|
|
1660
|
+
});
|
|
1661
|
+
log(` ✅ [${generator.name}] Generated ${file.path}`);
|
|
1662
|
+
}
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1665
|
+
log(` ❌ [${generator.name}] Failed ${schemaInfo.name}: ${err.message}`);
|
|
1666
|
+
result.errors.push({
|
|
1667
|
+
schema: schemaInfo.name,
|
|
1668
|
+
source: sourceName,
|
|
1669
|
+
error: err
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
const finalizeCtx = {
|
|
1675
|
+
processedSchemas,
|
|
1676
|
+
sources,
|
|
1677
|
+
rootDir
|
|
1678
|
+
};
|
|
1679
|
+
for (const generator of config.generators) if (generator.finalize) try {
|
|
1680
|
+
log(`\n[${generator.name}] Running finalize...`);
|
|
1681
|
+
const files = await generator.finalize(finalizeCtx);
|
|
1682
|
+
for (const file of files) for (const [sourceName, source] of Object.entries(sources)) {
|
|
1683
|
+
const fullPath = join(source.outputDir, file.path);
|
|
1684
|
+
if (!dryRun) {
|
|
1685
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
1686
|
+
writeFileSync(fullPath, file.content);
|
|
1687
|
+
}
|
|
1688
|
+
result.files.push({
|
|
1689
|
+
path: fullPath,
|
|
1690
|
+
source: sourceName
|
|
1691
|
+
});
|
|
1692
|
+
log(` ✅ [${generator.name}] Generated ${file.path} for ${sourceName}`);
|
|
1693
|
+
}
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1696
|
+
log(` ❌ [${generator.name}] Finalize failed: ${err.message}`);
|
|
1697
|
+
result.errors.push({ error: err });
|
|
1698
|
+
}
|
|
1699
|
+
if (config.afterAll) {
|
|
1700
|
+
log(`\n🔧 Running afterAll hook...`);
|
|
1701
|
+
const afterAllCtx = {
|
|
1702
|
+
sources,
|
|
1703
|
+
rootDir,
|
|
1704
|
+
processedSchemas,
|
|
1705
|
+
generatedFiles: result.files.map((f) => ({
|
|
1706
|
+
path: f.path,
|
|
1707
|
+
content: ""
|
|
1708
|
+
}))
|
|
1709
|
+
};
|
|
1710
|
+
const extraFiles = await config.afterAll(afterAllCtx);
|
|
1711
|
+
if (extraFiles && Array.isArray(extraFiles)) for (const file of extraFiles) {
|
|
1712
|
+
const fullPath = join(rootDir, file.path);
|
|
1713
|
+
if (!dryRun) {
|
|
1714
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
1715
|
+
writeFileSync(fullPath, file.content);
|
|
1716
|
+
}
|
|
1717
|
+
result.files.push({
|
|
1718
|
+
path: fullPath,
|
|
1719
|
+
source: "afterAll"
|
|
1720
|
+
});
|
|
1721
|
+
log(` ✅ [afterAll] Generated ${file.path}`);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
return result;
|
|
1725
|
+
}
|
|
1726
|
+
function createImportResolver(currentSchema, currentSource, allSources, config) {
|
|
1727
|
+
const ext = config.importExtension ?? "";
|
|
1728
|
+
return (schemaLocation) => {
|
|
1729
|
+
const schemaPath = schemaLocation.replace(/\.xsd$/, "");
|
|
1730
|
+
const schemaBaseName = schemaPath.replace(/^.*\//, "");
|
|
1731
|
+
const currentSchemaDir = dirname(currentSchema.name);
|
|
1732
|
+
const normalizedPath = (currentSchemaDir !== "." ? join(currentSchemaDir, schemaPath).replaceAll(/\\/g, "/") : schemaPath).split("/").reduce((acc, part) => {
|
|
1733
|
+
if (part === "..") acc.pop();
|
|
1734
|
+
else if (part !== ".") acc.push(part);
|
|
1735
|
+
return acc;
|
|
1736
|
+
}, []).join("/");
|
|
1737
|
+
if (currentSource.schemas.includes(normalizedPath)) {
|
|
1738
|
+
const fromDir = dirname(currentSchema.name);
|
|
1739
|
+
const toPath = normalizedPath;
|
|
1740
|
+
const relImport = fromDir === dirname(toPath) ? `./${basename(toPath)}${ext}` : relative(fromDir || ".", toPath).replaceAll(/\\/g, "/") || `./${basename(toPath)}`;
|
|
1741
|
+
return relImport.startsWith(".") ? `${relImport}${ext}` : `./${relImport}${ext}`;
|
|
1742
|
+
}
|
|
1743
|
+
if (currentSource.schemas.includes(schemaPath)) return `./${schemaPath}${ext}`;
|
|
1744
|
+
if (currentSource.schemas.includes(schemaBaseName)) return `./${schemaBaseName}${ext}`;
|
|
1745
|
+
for (const [sourceName, source] of Object.entries(allSources)) {
|
|
1746
|
+
if (sourceName === currentSource.name) continue;
|
|
1747
|
+
if (source.schemas.includes(normalizedPath)) return `${relative(currentSource.outputDir, source.outputDir)}/${normalizedPath}${ext}`.replaceAll(/\\/g, "/");
|
|
1748
|
+
if (source.schemas.includes(schemaPath)) return `${relative(currentSource.outputDir, source.outputDir)}/${schemaPath}${ext}`.replaceAll(/\\/g, "/");
|
|
1749
|
+
if (source.schemas.includes(schemaBaseName)) return `${relative(currentSource.outputDir, source.outputDir)}/${schemaBaseName}${ext}`.replaceAll(/\\/g, "/");
|
|
1750
|
+
}
|
|
1751
|
+
return null;
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
/**
|
|
1755
|
+
* Discover all dependent schemas by recursively parsing XSD files
|
|
1756
|
+
* and extracting schemaLocation from xs:import, xs:include, xs:redefine
|
|
1757
|
+
*/
|
|
1758
|
+
function discoverDependentSchemas(entrySchemas, xsdDir, log) {
|
|
1759
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
1760
|
+
const queue = [...entrySchemas];
|
|
1761
|
+
while (queue.length > 0) {
|
|
1762
|
+
const schemaName = queue.shift();
|
|
1763
|
+
if (!schemaName) continue;
|
|
1764
|
+
if (discovered.has(schemaName)) continue;
|
|
1765
|
+
const xsdPath = join(xsdDir, `${schemaName}.xsd`);
|
|
1766
|
+
if (!existsSync(xsdPath)) throw new Error(`Schema not found: ${schemaName}.xsd (resolved: ${xsdPath})`);
|
|
1767
|
+
discovered.add(schemaName);
|
|
1768
|
+
try {
|
|
1769
|
+
const xsdContent = readFileSync(xsdPath, "utf-8");
|
|
1770
|
+
const deps = extractSchemaLocations(parseXsd(xsdContent), xsdDir, schemaName);
|
|
1771
|
+
for (const dep of deps) if (!discovered.has(dep)) {
|
|
1772
|
+
queue.push(dep);
|
|
1773
|
+
log(` 🔗 Discovered dependency: ${dep} (from ${schemaName})`);
|
|
1774
|
+
}
|
|
1775
|
+
} catch {
|
|
1776
|
+
log(` ❌ Failed to parse ${schemaName} for dependency discovery`);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
return Array.from(discovered);
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Extract schema names from schemaLocation attributes
|
|
1783
|
+
*/
|
|
1784
|
+
function extractSchemaLocations(schema, xsdDir, currentSchema) {
|
|
1785
|
+
const locations = [];
|
|
1786
|
+
const currentDir = dirname(join(xsdDir, `${currentSchema}.xsd`));
|
|
1787
|
+
const imports = schema.import;
|
|
1788
|
+
if (imports) {
|
|
1789
|
+
for (const imp of imports) if (imp.schemaLocation) {
|
|
1790
|
+
const name = resolveSchemaName(imp.schemaLocation, currentDir, xsdDir);
|
|
1791
|
+
if (name) locations.push(name);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
const includes = schema.include;
|
|
1795
|
+
if (includes) {
|
|
1796
|
+
for (const inc of includes) if (inc.schemaLocation) {
|
|
1797
|
+
const name = resolveSchemaName(inc.schemaLocation, currentDir, xsdDir);
|
|
1798
|
+
if (name) locations.push(name);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
const redefines = schema.redefine;
|
|
1802
|
+
if (redefines) {
|
|
1803
|
+
for (const red of redefines) if (red.schemaLocation) {
|
|
1804
|
+
const name = resolveSchemaName(red.schemaLocation, currentDir, xsdDir);
|
|
1805
|
+
if (name) locations.push(name);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
return locations;
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* Resolve schemaLocation to a schema name relative to xsdDir
|
|
1812
|
+
*/
|
|
1813
|
+
function resolveSchemaName(schemaLocation, currentDir, xsdDir) {
|
|
1814
|
+
const fullPath = join(currentDir, schemaLocation);
|
|
1815
|
+
const name = relative(xsdDir, fullPath).replace(/\.xsd$/, "");
|
|
1816
|
+
if (existsSync(fullPath)) return name;
|
|
1817
|
+
return null;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
//#endregion
|
|
1821
|
+
export { getLocalName as _, loadAndLinkSchema as a, getSubstitutes as c, SchemaTraverser as d, resolveSchemaTypes as f, getChildElements as g, getAttributeValue as h, linkSchema as i, resolveSchema as l, getAllChildElements as m, createSchemaLoader as n, loadSchema as o, parseXsd as p, defaultLoader as r, parseSchemaContent as s, runCodegen as t, SchemaResolver as u, getTextContent as v };
|
|
1822
|
+
//# sourceMappingURL=runner-BSGqj8LM.mjs.map
|