@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,635 @@
|
|
|
1
|
+
import { l as resolveSchema, t as runCodegen } from "../runner-BSGqj8LM.mjs";
|
|
2
|
+
import { r as generateInterfaces, t as defineConfig } from "../types-Ch0GQQ5n.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/generators/raw-schema.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create a raw schema generator plugin
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { rawSchema } from '@abapify/ts-xsd/generators';
|
|
11
|
+
*
|
|
12
|
+
* export default defineConfig({
|
|
13
|
+
* generators: [
|
|
14
|
+
* rawSchema(), // default export
|
|
15
|
+
* rawSchema({ namedExport: 'mySchema' }), // named export
|
|
16
|
+
* ],
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function rawSchema(options = {}) {
|
|
21
|
+
const { defaultExport = true, namedExport, $xmlns = true, $imports = true, $includes = true, $filename = false, exclude = ["annotation"], header = true, resolveIncludes = false, resolveAll = false, isolatedDeclarations = false, singleQuote = false } = options;
|
|
22
|
+
return {
|
|
23
|
+
name: "raw-schema",
|
|
24
|
+
transform(ctx) {
|
|
25
|
+
const { schema, source } = ctx;
|
|
26
|
+
let schemaToProcess = schema.schema;
|
|
27
|
+
if (options.resolve) {
|
|
28
|
+
const resolveOpts = options.resolve === true ? {} : options.resolve;
|
|
29
|
+
schemaToProcess = resolveSchema(schema.schema, resolveOpts);
|
|
30
|
+
}
|
|
31
|
+
if (resolveAll) schemaToProcess = resolveSchema(schema.schema, { filterToRootElements: true });
|
|
32
|
+
else if (resolveIncludes) schemaToProcess = resolveSchema(schema.schema, {
|
|
33
|
+
resolveImports: false,
|
|
34
|
+
keepImportsRef: true
|
|
35
|
+
});
|
|
36
|
+
const outputSchema = buildOutputSchema(schemaToProcess, {
|
|
37
|
+
$xmlns,
|
|
38
|
+
$imports: resolveAll ? false : $imports,
|
|
39
|
+
$includes: resolveAll || resolveIncludes ? false : $includes,
|
|
40
|
+
$filename,
|
|
41
|
+
exclude,
|
|
42
|
+
schemaName: schema.name,
|
|
43
|
+
resolveImport: ctx.resolveImport
|
|
44
|
+
});
|
|
45
|
+
const lines = [];
|
|
46
|
+
if (header) lines.push("/**", " * Auto-generated schema from XSD", " * ", " * DO NOT EDIT - Generated by ts-xsd codegen", ` * Source: ${source.name}/${schema.name}.xsd`, " */", "");
|
|
47
|
+
if (isolatedDeclarations) lines.push("import type { Schema } from '@abapify/ts-xsd';");
|
|
48
|
+
const shouldGenerateImports = !options.resolve && !resolveIncludes && !resolveAll && ($imports || $includes);
|
|
49
|
+
if (shouldGenerateImports) {
|
|
50
|
+
const imports = getSchemaImports(schema.schema, ctx.resolveImport, {
|
|
51
|
+
$imports,
|
|
52
|
+
$includes
|
|
53
|
+
});
|
|
54
|
+
if (imports.length > 0) for (const imp of imports) if (isolatedDeclarations) lines.push(`import { schema as ${imp.name} } from '${imp.path}';`);
|
|
55
|
+
else lines.push(`import ${imp.name} from '${imp.path}';`);
|
|
56
|
+
}
|
|
57
|
+
if (isolatedDeclarations || shouldGenerateImports && getSchemaImports(schema.schema, ctx.resolveImport, {
|
|
58
|
+
$imports,
|
|
59
|
+
$includes
|
|
60
|
+
}).length > 0) lines.push("");
|
|
61
|
+
const literal = objectToLiteral(outputSchema, true, " ", 0, singleQuote);
|
|
62
|
+
if (isolatedDeclarations) {
|
|
63
|
+
const exportName = namedExport ?? "schema";
|
|
64
|
+
lines.push(`export const ${exportName} = ${literal} as const satisfies Schema;`);
|
|
65
|
+
if (defaultExport && !namedExport) lines.push(`export default ${exportName};`);
|
|
66
|
+
} else if (namedExport) lines.push(`export const ${namedExport} = ${literal} as const;`);
|
|
67
|
+
else if (defaultExport) lines.push(`export default ${literal} as const;`);
|
|
68
|
+
lines.push("");
|
|
69
|
+
return [{
|
|
70
|
+
path: `${schema.name}.ts`,
|
|
71
|
+
content: lines.join("\n")
|
|
72
|
+
}];
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function buildOutputSchema(schema, options) {
|
|
77
|
+
const result = {};
|
|
78
|
+
const excludeSet = new Set(options.exclude);
|
|
79
|
+
if (options.$xmlns && schema.$xmlns) result.$xmlns = schema.$xmlns;
|
|
80
|
+
if (options.$imports && schema.import) {
|
|
81
|
+
const importRefs = [];
|
|
82
|
+
const imports = schema.import;
|
|
83
|
+
for (const imp of imports) if (imp.schemaLocation) {
|
|
84
|
+
if (!options.resolveImport(imp.schemaLocation)) throw new Error(`Cannot resolve xs:import schemaLocation: ${imp.schemaLocation} (in ${options.schemaName}.xsd)`);
|
|
85
|
+
const name = imp.schemaLocation.replace(/\.xsd$/, "").replace(/^.*\//, "");
|
|
86
|
+
importRefs.push(new SchemaRef(name));
|
|
87
|
+
}
|
|
88
|
+
if (importRefs.length > 0) result.$imports = importRefs;
|
|
89
|
+
}
|
|
90
|
+
if (options.$includes && schema.include) {
|
|
91
|
+
const includeRefs = [];
|
|
92
|
+
const includes = schema.include;
|
|
93
|
+
for (const inc of includes) if (inc.schemaLocation) {
|
|
94
|
+
if (!options.resolveImport(inc.schemaLocation)) throw new Error(`Cannot resolve xs:include schemaLocation: ${inc.schemaLocation} (in ${options.schemaName}.xsd)`);
|
|
95
|
+
const name = inc.schemaLocation.replace(/\.xsd$/, "").replace(/^.*\//, "");
|
|
96
|
+
includeRefs.push(new SchemaRef(name));
|
|
97
|
+
}
|
|
98
|
+
if (includeRefs.length > 0) result.$includes = includeRefs;
|
|
99
|
+
}
|
|
100
|
+
if (options.$filename) result.$filename = `${options.schemaName}.xsd`;
|
|
101
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
102
|
+
if (key === "$xmlns" || key === "$imports" || key === "$includes" || key === "$filename") continue;
|
|
103
|
+
if (key === "import" && options.$imports) continue;
|
|
104
|
+
if (key === "include" && options.$includes) continue;
|
|
105
|
+
if (!excludeSet.has(key)) result[key] = filterDeep(value, excludeSet);
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function getSchemaImports(schema, resolveImport, options = {
|
|
110
|
+
$imports: true,
|
|
111
|
+
$includes: true
|
|
112
|
+
}) {
|
|
113
|
+
const imports = [];
|
|
114
|
+
if (options.$imports) {
|
|
115
|
+
const xsdImports = schema.import;
|
|
116
|
+
if (xsdImports) {
|
|
117
|
+
for (const imp of xsdImports) if (imp.schemaLocation) {
|
|
118
|
+
const modulePath = resolveImport(imp.schemaLocation);
|
|
119
|
+
if (modulePath) {
|
|
120
|
+
const name = imp.schemaLocation.replace(/\.xsd$/, "").replace(/^.*\//, "");
|
|
121
|
+
imports.push({
|
|
122
|
+
name,
|
|
123
|
+
path: modulePath
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (options.$includes) {
|
|
130
|
+
const xsdIncludes = schema.include;
|
|
131
|
+
if (xsdIncludes) {
|
|
132
|
+
for (const inc of xsdIncludes) if (inc.schemaLocation) {
|
|
133
|
+
const modulePath = resolveImport(inc.schemaLocation);
|
|
134
|
+
if (modulePath) {
|
|
135
|
+
const name = inc.schemaLocation.replace(/\.xsd$/, "").replace(/^.*\//, "");
|
|
136
|
+
imports.push({
|
|
137
|
+
name,
|
|
138
|
+
path: modulePath
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return imports;
|
|
145
|
+
}
|
|
146
|
+
/** Marker class for schema references in $imports */
|
|
147
|
+
var SchemaRef = class {
|
|
148
|
+
name;
|
|
149
|
+
constructor(name) {
|
|
150
|
+
this.name = name;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Format a string value as a single-quoted literal, unescaping JSON double quotes
|
|
155
|
+
* and escaping any single quotes.
|
|
156
|
+
*/
|
|
157
|
+
function toSingleQuoteLiteral(jsonStr) {
|
|
158
|
+
return `'${jsonStr.slice(1, -1).replaceAll("\\\"", "\"").replaceAll("'", "\\'")}'`;
|
|
159
|
+
}
|
|
160
|
+
function filterDeep(value, exclude) {
|
|
161
|
+
if (value instanceof SchemaRef) return value;
|
|
162
|
+
if (Array.isArray(value)) return value.map((item) => filterDeep(item, exclude));
|
|
163
|
+
if (value !== null && typeof value === "object") {
|
|
164
|
+
const result = {};
|
|
165
|
+
for (const [k, v] of Object.entries(value)) if (!exclude.has(k)) result[k] = filterDeep(v, exclude);
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
function objectToLiteral(value, pretty, indent, depth, singleQuote = false) {
|
|
171
|
+
if (value === null || value === void 0) return "undefined";
|
|
172
|
+
if (value instanceof SchemaRef) return value.name;
|
|
173
|
+
if (typeof value === "string") {
|
|
174
|
+
const json = JSON.stringify(value);
|
|
175
|
+
if (singleQuote) return toSingleQuoteLiteral(json);
|
|
176
|
+
return json;
|
|
177
|
+
}
|
|
178
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
179
|
+
if (Array.isArray(value)) {
|
|
180
|
+
if (value.length === 0) return "[]";
|
|
181
|
+
const items = value.map((item) => objectToLiteral(item, pretty, indent, depth + 1, singleQuote));
|
|
182
|
+
if (pretty) {
|
|
183
|
+
const itemIndent = indent.repeat(depth + 1);
|
|
184
|
+
const closeIndent = indent.repeat(depth);
|
|
185
|
+
return `[\n${items.map((item) => `${itemIndent}${item}`).join(",\n")},\n${closeIndent}]`;
|
|
186
|
+
}
|
|
187
|
+
return `[${items.join(", ")}]`;
|
|
188
|
+
}
|
|
189
|
+
if (typeof value === "object") {
|
|
190
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0);
|
|
191
|
+
if (entries.length === 0) return "{}";
|
|
192
|
+
const props = entries.map(([key, val]) => {
|
|
193
|
+
let keyStr = key;
|
|
194
|
+
if (!isValidIdentifier(key)) {
|
|
195
|
+
const json = JSON.stringify(key);
|
|
196
|
+
keyStr = singleQuote ? toSingleQuoteLiteral(json) : json;
|
|
197
|
+
}
|
|
198
|
+
const valStr = objectToLiteral(val, pretty, indent, depth + 1, singleQuote);
|
|
199
|
+
return `${keyStr}: ${valStr}`;
|
|
200
|
+
});
|
|
201
|
+
if (pretty) {
|
|
202
|
+
const propIndent = indent.repeat(depth + 1);
|
|
203
|
+
const closeIndent = indent.repeat(depth);
|
|
204
|
+
return `{\n${props.map((prop) => `${propIndent}${prop}`).join(",\n")},\n${closeIndent}}`;
|
|
205
|
+
}
|
|
206
|
+
return `{ ${props.join(", ")} }`;
|
|
207
|
+
}
|
|
208
|
+
return "undefined";
|
|
209
|
+
}
|
|
210
|
+
const RESERVED_WORDS$2 = /* @__PURE__ */ new Set([
|
|
211
|
+
"break",
|
|
212
|
+
"case",
|
|
213
|
+
"catch",
|
|
214
|
+
"continue",
|
|
215
|
+
"debugger",
|
|
216
|
+
"default",
|
|
217
|
+
"delete",
|
|
218
|
+
"do",
|
|
219
|
+
"else",
|
|
220
|
+
"finally",
|
|
221
|
+
"for",
|
|
222
|
+
"function",
|
|
223
|
+
"if",
|
|
224
|
+
"in",
|
|
225
|
+
"instanceof",
|
|
226
|
+
"new",
|
|
227
|
+
"return",
|
|
228
|
+
"switch",
|
|
229
|
+
"this",
|
|
230
|
+
"throw",
|
|
231
|
+
"try",
|
|
232
|
+
"typeof",
|
|
233
|
+
"var",
|
|
234
|
+
"void",
|
|
235
|
+
"while",
|
|
236
|
+
"with",
|
|
237
|
+
"class",
|
|
238
|
+
"const",
|
|
239
|
+
"enum",
|
|
240
|
+
"export",
|
|
241
|
+
"extends",
|
|
242
|
+
"import",
|
|
243
|
+
"super",
|
|
244
|
+
"implements",
|
|
245
|
+
"interface",
|
|
246
|
+
"let",
|
|
247
|
+
"package",
|
|
248
|
+
"private",
|
|
249
|
+
"protected",
|
|
250
|
+
"public",
|
|
251
|
+
"static",
|
|
252
|
+
"yield",
|
|
253
|
+
"await",
|
|
254
|
+
"null",
|
|
255
|
+
"true",
|
|
256
|
+
"false"
|
|
257
|
+
]);
|
|
258
|
+
function isValidIdentifier(str) {
|
|
259
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str) && !RESERVED_WORDS$2.has(str);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region src/generators/inferred-types.ts
|
|
264
|
+
/**
|
|
265
|
+
* Create an inferred types generator plugin
|
|
266
|
+
*
|
|
267
|
+
* This generator should be used AFTER rawSchema() in the pipeline.
|
|
268
|
+
* It modifies the generated schema file to add type exports.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```ts
|
|
272
|
+
* import { rawSchema, inferredTypes } from '@abapify/ts-xsd/generators';
|
|
273
|
+
*
|
|
274
|
+
* export default defineConfig({
|
|
275
|
+
* generators: [
|
|
276
|
+
* rawSchema(),
|
|
277
|
+
* inferredTypes(), // Adds: export type Intf = InferSchema<typeof schema>;
|
|
278
|
+
* ],
|
|
279
|
+
* });
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
function inferredTypes(options = {}) {
|
|
283
|
+
const { inferSchemaImport = "ts-xsd", typeNamePattern = "{Name}" } = options;
|
|
284
|
+
return {
|
|
285
|
+
name: "inferred-types",
|
|
286
|
+
transform(ctx) {
|
|
287
|
+
const { schema } = ctx;
|
|
288
|
+
const typeName = typeNamePattern.replace("{Name}", pascalCase$1(schema.name));
|
|
289
|
+
const lines = [
|
|
290
|
+
"/**",
|
|
291
|
+
" * Auto-generated type from schema",
|
|
292
|
+
" * ",
|
|
293
|
+
" * DO NOT EDIT - Generated by ts-xsd codegen",
|
|
294
|
+
" */",
|
|
295
|
+
"",
|
|
296
|
+
`import type { InferSchema } from '${inferSchemaImport}';`,
|
|
297
|
+
`import schema from './${schema.name}';`,
|
|
298
|
+
"",
|
|
299
|
+
`/** Inferred type for ${schema.name} schema */`,
|
|
300
|
+
`export type ${typeName} = InferSchema<typeof schema>;`,
|
|
301
|
+
"",
|
|
302
|
+
`export default schema;`,
|
|
303
|
+
""
|
|
304
|
+
];
|
|
305
|
+
return [{
|
|
306
|
+
path: `${schema.name}.typed.ts`,
|
|
307
|
+
content: lines.join("\n")
|
|
308
|
+
}];
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function pascalCase$1(str) {
|
|
313
|
+
return str.replaceAll(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (s) => s.toUpperCase());
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/generators/interfaces.ts
|
|
318
|
+
/**
|
|
319
|
+
* Extract base name from schema name (handles paths like 'sap/atom' -> 'atom')
|
|
320
|
+
*/
|
|
321
|
+
function getBaseName(schemaName) {
|
|
322
|
+
return schemaName.split("/").at(-1) ?? schemaName;
|
|
323
|
+
}
|
|
324
|
+
function deriveRootTypeName(schemaName, pattern) {
|
|
325
|
+
const baseName = getBaseName(schemaName);
|
|
326
|
+
const capitalized = baseName.charAt(0).toUpperCase() + baseName.slice(1);
|
|
327
|
+
return pattern.replace("{name}", baseName).replace("{Name}", capitalized);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Create an interfaces generator plugin
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```ts
|
|
334
|
+
* import { interfaces } from '@abapify/ts-xsd/generators';
|
|
335
|
+
*
|
|
336
|
+
* export default defineConfig({
|
|
337
|
+
* generators: [
|
|
338
|
+
* interfaces(), // interfaces with imports
|
|
339
|
+
* interfaces({ flatten: true }), // fully flattened types
|
|
340
|
+
* ],
|
|
341
|
+
* });
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
function interfaces(options = {}) {
|
|
345
|
+
const { filePattern = options.flatten ? "{name}.flattened.ts" : "{name}.types.ts", header = true, flatten = false, rootTypePattern = "{Name}Schema", addJsDoc = true } = options;
|
|
346
|
+
return {
|
|
347
|
+
name: "interfaces",
|
|
348
|
+
transform(ctx) {
|
|
349
|
+
const { schema, source } = ctx;
|
|
350
|
+
const rootTypeName = deriveRootTypeName(schema.name, rootTypePattern);
|
|
351
|
+
const targetSchema = resolveSchema(schema.schema, { keepImportsRef: true });
|
|
352
|
+
const hasRootElements = targetSchema.element && targetSchema.element.length > 0;
|
|
353
|
+
if (flatten && !hasRootElements) return [];
|
|
354
|
+
const { code } = generateInterfaces(targetSchema, {
|
|
355
|
+
rootTypeName: flatten && hasRootElements ? rootTypeName : null,
|
|
356
|
+
addJsDoc,
|
|
357
|
+
flatten
|
|
358
|
+
});
|
|
359
|
+
const lines = [];
|
|
360
|
+
if (header) lines.push("/**", " * Auto-generated TypeScript interfaces from XSD", " * DO NOT EDIT - Generated by ts-xsd codegen", ` * Source: ${source.name}/${schema.name}.xsd`, flatten ? " * Mode: Flattened" : " * Mode: Interfaces", " */", "");
|
|
361
|
+
lines.push(code);
|
|
362
|
+
return [{
|
|
363
|
+
path: filePattern.replace("{name}", schema.name).replace("{source}", source.name),
|
|
364
|
+
content: lines.join("\n")
|
|
365
|
+
}];
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
//#endregion
|
|
371
|
+
//#region src/generators/index-barrel.ts
|
|
372
|
+
/**
|
|
373
|
+
* Create an index barrel generator plugin
|
|
374
|
+
*
|
|
375
|
+
* This generator runs in finalize() after all schemas are processed.
|
|
376
|
+
* It generates an index.ts that re-exports all schemas.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* import { rawSchema, indexBarrel } from '@abapify/ts-xsd/generators';
|
|
381
|
+
*
|
|
382
|
+
* export default defineConfig({
|
|
383
|
+
* generators: [
|
|
384
|
+
* rawSchema(),
|
|
385
|
+
* indexBarrel(), // Generates index.ts with all exports
|
|
386
|
+
* ],
|
|
387
|
+
* });
|
|
388
|
+
* ```
|
|
389
|
+
*/
|
|
390
|
+
function indexBarrel(options = {}) {
|
|
391
|
+
const { filename = "index.ts", importExtension = "", namedExports = false, includeTypedExports = false, header = true } = options;
|
|
392
|
+
return {
|
|
393
|
+
name: "index-barrel",
|
|
394
|
+
finalize(ctx) {
|
|
395
|
+
const files = [];
|
|
396
|
+
for (const [sourceName, schemas] of ctx.processedSchemas) {
|
|
397
|
+
if (!ctx.sources[sourceName]) continue;
|
|
398
|
+
const lines = [];
|
|
399
|
+
if (header) lines.push("/**", ` * Auto-generated index for ${sourceName} schemas`, " * ", " * DO NOT EDIT - Generated by ts-xsd codegen", " */", "");
|
|
400
|
+
const sortedSchemas = [...schemas].sort((a, b) => a.name.localeCompare(b.name));
|
|
401
|
+
for (const schema of sortedSchemas) {
|
|
402
|
+
if (namedExports) {
|
|
403
|
+
const exportName = toValidIdentifier$1(schema.name);
|
|
404
|
+
lines.push(`export { default as ${exportName} } from './${schema.name}${importExtension}';`);
|
|
405
|
+
} else lines.push(`export * from './${schema.name}${importExtension}';`);
|
|
406
|
+
if (includeTypedExports) {
|
|
407
|
+
const typeName = pascalCase(schema.name);
|
|
408
|
+
lines.push(`export type { ${typeName} } from './${schema.name}.typed${importExtension}';`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
lines.push("");
|
|
412
|
+
files.push({
|
|
413
|
+
path: filename,
|
|
414
|
+
content: lines.join("\n")
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
return files;
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
const RESERVED_WORDS$1 = /* @__PURE__ */ new Set([
|
|
422
|
+
"break",
|
|
423
|
+
"case",
|
|
424
|
+
"catch",
|
|
425
|
+
"continue",
|
|
426
|
+
"debugger",
|
|
427
|
+
"default",
|
|
428
|
+
"delete",
|
|
429
|
+
"do",
|
|
430
|
+
"else",
|
|
431
|
+
"finally",
|
|
432
|
+
"for",
|
|
433
|
+
"function",
|
|
434
|
+
"if",
|
|
435
|
+
"in",
|
|
436
|
+
"instanceof",
|
|
437
|
+
"new",
|
|
438
|
+
"return",
|
|
439
|
+
"switch",
|
|
440
|
+
"this",
|
|
441
|
+
"throw",
|
|
442
|
+
"try",
|
|
443
|
+
"typeof",
|
|
444
|
+
"var",
|
|
445
|
+
"void",
|
|
446
|
+
"while",
|
|
447
|
+
"with",
|
|
448
|
+
"class",
|
|
449
|
+
"const",
|
|
450
|
+
"enum",
|
|
451
|
+
"export",
|
|
452
|
+
"extends",
|
|
453
|
+
"import",
|
|
454
|
+
"super",
|
|
455
|
+
"implements",
|
|
456
|
+
"interface",
|
|
457
|
+
"let",
|
|
458
|
+
"package",
|
|
459
|
+
"private",
|
|
460
|
+
"protected",
|
|
461
|
+
"public",
|
|
462
|
+
"static",
|
|
463
|
+
"yield",
|
|
464
|
+
"await",
|
|
465
|
+
"null",
|
|
466
|
+
"true",
|
|
467
|
+
"false"
|
|
468
|
+
]);
|
|
469
|
+
function toValidIdentifier$1(name) {
|
|
470
|
+
let result = name.replaceAll(/-([a-zA-Z])/g, (_, c) => c.toUpperCase());
|
|
471
|
+
if (RESERVED_WORDS$1.has(result)) result = `${result}_`;
|
|
472
|
+
return result;
|
|
473
|
+
}
|
|
474
|
+
function pascalCase(str) {
|
|
475
|
+
return str.replaceAll(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (s) => s.toUpperCase());
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
//#endregion
|
|
479
|
+
//#region src/generators/typed-schemas.ts
|
|
480
|
+
/**
|
|
481
|
+
* Create a typed schemas generator plugin
|
|
482
|
+
*
|
|
483
|
+
* This generator runs in finalize() after all schemas are processed.
|
|
484
|
+
* It generates a single index file that exports typed schema wrappers.
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```ts
|
|
488
|
+
* import { rawSchema, interfaces, typedSchemas } from '@abapify/ts-xsd/generators';
|
|
489
|
+
*
|
|
490
|
+
* export default defineConfig({
|
|
491
|
+
* generators: [
|
|
492
|
+
* rawSchema(),
|
|
493
|
+
* interfaces(),
|
|
494
|
+
* typedSchemas(), // Generates typed wrappers
|
|
495
|
+
* ],
|
|
496
|
+
* });
|
|
497
|
+
* ```
|
|
498
|
+
*/
|
|
499
|
+
function typedSchemas(options = {}) {
|
|
500
|
+
const { outputPath = "src/schemas/generated/index.ts", tsXsdPath = "ts-xsd", schemasPath = "./schemas", typesPath = "./types", header = true } = options;
|
|
501
|
+
return {
|
|
502
|
+
name: "typed-schemas",
|
|
503
|
+
finalize(ctx) {
|
|
504
|
+
const lines = [];
|
|
505
|
+
if (header) lines.push("/**", " * Auto-generated typed schema exports", " * ", " * DO NOT EDIT - Generated by ts-xsd codegen", " * ", " * These schemas have pre-computed types for full type inference.", " * Use these instead of raw schemas for type-safe parsing/building.", " * ", " * @example", " * import { classes } from '@abapify/adt-schemas';", " * const data = classes.parse(xml); // data is fully typed!", " */", "");
|
|
506
|
+
lines.push(`import { typedSchema, type TypedSchema } from '${tsXsdPath}';`);
|
|
507
|
+
lines.push("");
|
|
508
|
+
const schemaExports = [];
|
|
509
|
+
for (const [sourceName, schemas] of ctx.processedSchemas) {
|
|
510
|
+
lines.push(`// ${sourceName.toUpperCase()} schemas`);
|
|
511
|
+
for (const schema of schemas) {
|
|
512
|
+
const rootTypes = extractRootTypes(schema);
|
|
513
|
+
if (rootTypes.length === 0) {
|
|
514
|
+
lines.push(`// ${schema.name}: No root elements found, skipping typed export`);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
schemaExports.push({
|
|
518
|
+
name: schema.name,
|
|
519
|
+
sourceName,
|
|
520
|
+
rootTypes
|
|
521
|
+
});
|
|
522
|
+
const exportName = toValidIdentifier(schema.name);
|
|
523
|
+
const schemaImport = `${schemasPath}/${sourceName}/${schema.name}`;
|
|
524
|
+
const typesImport = `${typesPath}/${sourceName}/${schema.name}.types`;
|
|
525
|
+
lines.push(`import _${exportName} from '${schemaImport}';`);
|
|
526
|
+
if (rootTypes.length === 1) lines.push(`import type { ${rootTypes[0]} } from '${typesImport}';`);
|
|
527
|
+
else lines.push(`import type { ${rootTypes.join(", ")} } from '${typesImport}';`);
|
|
528
|
+
}
|
|
529
|
+
lines.push("");
|
|
530
|
+
}
|
|
531
|
+
lines.push("// ============================================================================");
|
|
532
|
+
lines.push("// TYPED SCHEMA EXPORTS");
|
|
533
|
+
lines.push("// ============================================================================");
|
|
534
|
+
lines.push("");
|
|
535
|
+
for (const { name, rootTypes } of schemaExports) {
|
|
536
|
+
const exportName = toValidIdentifier(name);
|
|
537
|
+
if (rootTypes.length === 1) lines.push(`export const ${exportName}: TypedSchema<${rootTypes[0]}> = typedSchema<${rootTypes[0]}>(_${exportName});`);
|
|
538
|
+
else {
|
|
539
|
+
const unionType = rootTypes.join(" | ");
|
|
540
|
+
lines.push(`export const ${exportName}: TypedSchema<${unionType}> = typedSchema<${unionType}>(_${exportName});`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
lines.push("");
|
|
544
|
+
lines.push("// Types are internal - import directly from types/{source}/{schema}.types if needed");
|
|
545
|
+
lines.push("");
|
|
546
|
+
return [{
|
|
547
|
+
path: outputPath,
|
|
548
|
+
content: lines.join("\n")
|
|
549
|
+
}];
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Extract root element type names from a schema
|
|
555
|
+
*/
|
|
556
|
+
function extractRootTypes(schema) {
|
|
557
|
+
const types = [];
|
|
558
|
+
const elements = schema.schema.element;
|
|
559
|
+
if (!elements || elements.length === 0) return types;
|
|
560
|
+
for (const element of elements) if (element.type) {
|
|
561
|
+
const typeName = stripNamespacePrefix(element.type);
|
|
562
|
+
types.push(toPascalCase(typeName));
|
|
563
|
+
} else if (element.name) types.push(toPascalCase(element.name));
|
|
564
|
+
return types;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Strip namespace prefix from QName (e.g., "ns:TypeName" -> "TypeName")
|
|
568
|
+
*/
|
|
569
|
+
function stripNamespacePrefix(qname) {
|
|
570
|
+
const colonIndex = qname.indexOf(":");
|
|
571
|
+
return colonIndex >= 0 ? qname.slice(colonIndex + 1) : qname;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Convert to PascalCase
|
|
575
|
+
*/
|
|
576
|
+
function toPascalCase(str) {
|
|
577
|
+
return str.replaceAll(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (s) => s.toUpperCase());
|
|
578
|
+
}
|
|
579
|
+
const RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
580
|
+
"break",
|
|
581
|
+
"case",
|
|
582
|
+
"catch",
|
|
583
|
+
"continue",
|
|
584
|
+
"debugger",
|
|
585
|
+
"default",
|
|
586
|
+
"delete",
|
|
587
|
+
"do",
|
|
588
|
+
"else",
|
|
589
|
+
"finally",
|
|
590
|
+
"for",
|
|
591
|
+
"function",
|
|
592
|
+
"if",
|
|
593
|
+
"in",
|
|
594
|
+
"instanceof",
|
|
595
|
+
"new",
|
|
596
|
+
"return",
|
|
597
|
+
"switch",
|
|
598
|
+
"this",
|
|
599
|
+
"throw",
|
|
600
|
+
"try",
|
|
601
|
+
"typeof",
|
|
602
|
+
"var",
|
|
603
|
+
"void",
|
|
604
|
+
"while",
|
|
605
|
+
"with",
|
|
606
|
+
"class",
|
|
607
|
+
"const",
|
|
608
|
+
"enum",
|
|
609
|
+
"export",
|
|
610
|
+
"extends",
|
|
611
|
+
"import",
|
|
612
|
+
"super",
|
|
613
|
+
"implements",
|
|
614
|
+
"interface",
|
|
615
|
+
"let",
|
|
616
|
+
"package",
|
|
617
|
+
"private",
|
|
618
|
+
"protected",
|
|
619
|
+
"public",
|
|
620
|
+
"static",
|
|
621
|
+
"yield",
|
|
622
|
+
"await",
|
|
623
|
+
"null",
|
|
624
|
+
"true",
|
|
625
|
+
"false"
|
|
626
|
+
]);
|
|
627
|
+
function toValidIdentifier(name) {
|
|
628
|
+
let result = name.replaceAll(/-([a-zA-Z])/g, (_, c) => c.toUpperCase());
|
|
629
|
+
if (RESERVED_WORDS.has(result)) result = `${result}_`;
|
|
630
|
+
return result;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
//#endregion
|
|
634
|
+
export { defineConfig, indexBarrel, inferredTypes, interfaces, rawSchema, runCodegen, typedSchemas };
|
|
635
|
+
//# sourceMappingURL=index.mjs.map
|