@abapify/ts-xsd 0.4.34 → 0.4.36

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 CHANGED
@@ -1,338 +1,35 @@
1
- # ts-xsd
1
+ # @abapify/ts-xsd
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@abapify/ts-xsd)](https://www.npmjs.com/package/@abapify/ts-xsd)
4
- [![license](https://img.shields.io/npm/l/@abapify/ts-xsd)](https://github.com/abapify/adt-cli/blob/main/LICENSE)
3
+ [![npm](https://img.shields.io/npm/v/%40abapify%2Fts-xsd)](https://www.npmjs.com/package/@abapify/ts-xsd)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/abapify/adt-cli/blob/main/LICENSE)
5
5
 
6
- **W3C XSD 1.1 parser, builder, and TypeScript type inference** — a 1:1 TypeScript representation of XML Schema Definition.
6
+ W3C XSD 1.1 parser, builder, and TypeScript type inference.
7
7
 
8
- ## Why
9
-
10
- Working with XSD schemas in TypeScript usually means hand-maintaining types that drift from the source of truth. `ts-xsd` closes that gap:
11
-
12
- - **Parse** any W3C XSD 1.1 schema into a typed `Schema` object — no invented properties, no shortcuts.
13
- - **Infer** TypeScript types at compile time from schema literals via `InferSchema<T>`.
14
- - **Generate** TypeScript interfaces at build time when schemas are too complex for compile-time inference.
15
- - **Round-trip** `XSD → Schema → XSD` with semantic preservation.
16
- - **Parse/build XML** documents against a schema definition.
17
-
18
- ## Installation
8
+ ## Install
19
9
 
20
10
  ```bash
21
11
  npm install @abapify/ts-xsd
22
- # or
23
- pnpm add @abapify/ts-xsd
24
- # or
25
- bun add @abapify/ts-xsd
26
12
  ```
27
13
 
28
- > **Runtime dependency:** `@xmldom/xmldom` (DOM parsing). `ts-morph` and `zod` are required for codegen features.
14
+ ## Usage
29
15
 
30
- ## Quick Start
31
-
32
- ### Parse and Build XSD
33
-
34
- ```typescript
16
+ ```ts
35
17
  import { parseXsd, buildXsd } from '@abapify/ts-xsd';
36
18
 
37
- // Parse XSD to typed Schema object
38
- const schema = parseXsd(`
39
- <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
40
- <xs:element name="person" type="PersonType"/>
41
- <xs:complexType name="PersonType">
42
- <xs:sequence>
43
- <xs:element name="name" type="xs:string"/>
44
- <xs:element name="age" type="xs:int" minOccurs="0"/>
45
- </xs:sequence>
46
- </xs:complexType>
47
- </xs:schema>
48
- `);
49
-
50
- // Build back to XSD
51
- const xsd = buildXsd(schema, { pretty: true });
52
- ```
53
-
54
- ### Type Inference from Schema Literals
55
-
56
- ```typescript
57
- import type { InferSchema } from '@abapify/ts-xsd';
58
-
59
- const personSchema = {
60
- element: [{ name: 'person', type: 'PersonType' }],
61
- complexType: [
62
- {
63
- name: 'PersonType',
64
- sequence: {
65
- element: [
66
- { name: 'name', type: 'xs:string' },
67
- { name: 'age', type: 'xs:int', minOccurs: 0 },
68
- ],
69
- },
70
- },
71
- ],
72
- } as const;
73
-
74
- // Infer TypeScript type at compile time — no runtime cost
75
- type Person = InferSchema<typeof personSchema>;
76
- // => { name: string; age?: number }
77
- ```
78
-
79
- ### Parse and Build XML with a Schema
80
-
81
- ```typescript
82
- import { parseXml, buildXml } from '@abapify/ts-xsd';
83
-
84
- const xml = `<person><name>John</name><age>30</age></person>`;
85
- const data = parseXml(personSchema, xml);
86
- // => { person: { name: 'John', age: 30 } }
87
-
88
- const rebuilt = buildXml(personSchema, data, { xmlDecl: false });
89
- // => <person><name>John</name><age>30</age></person>
90
- ```
91
-
92
- ### Generate TypeScript Interfaces from XSD
93
-
94
- ```typescript
95
- import { parseXsd, generateInterfaces } from '@abapify/ts-xsd';
96
-
97
- const schema = parseXsd(xsdContent);
98
- const { code } = generateInterfaces(schema, {
99
- flatten: true, // Inline all nested types into one
100
- addJsDoc: true, // Add JSDoc comments
101
- rootTypeName: 'MySchema',
102
- });
103
-
104
- // code is a ready-to-write .ts file:
105
- // export type MySchema = { person: { name: string; age?: number } };
106
- ```
107
-
108
- ## API Reference
109
-
110
- ### XSD Module
111
-
112
- ```typescript
113
- import {
114
- parseXsd,
115
- buildXsd,
116
- resolveImports,
117
- loadSchema,
118
- type Schema,
119
- } from '@abapify/ts-xsd';
120
- ```
121
-
122
- #### `parseXsd(xsd: string): Schema`
123
-
124
- Parse an XSD XML string into a typed `Schema` object.
125
-
126
- #### `buildXsd(schema: Schema, options?: BuildOptions): string`
127
-
128
- Build an XSD XML string from a `Schema` object.
129
-
130
- ```typescript
131
- const xsd = buildXsd(schema, {
132
- prefix: 'xs', // Namespace prefix (default: 'xs')
133
- pretty: true, // Pretty print (default: true)
134
- indent: ' ', // Indentation string
135
- });
136
- ```
137
-
138
- #### `resolveImports(schema, availableSchemas): Schema`
139
-
140
- Resolve and link imported schemas by matching `import.schemaLocation` to `$filename`. Returns a new schema with `$imports` populated.
141
-
142
- ```typescript
143
- const base = { ...parseXsd(baseXsd), $filename: 'base.xsd' };
144
- const orders = { ...parseXsd(ordersXsd), $filename: 'orders.xsd' };
145
-
146
- const linked = resolveImports(orders, [base]);
147
- // linked.$imports = [base]
148
- ```
149
-
150
- #### `loadSchema(schemaPath: string, options?: LoaderOptions): Schema`
151
-
152
- Load and parse an XSD file from disk. Synchronous. Set `autoLink: true` to automatically resolve imports, or `autoResolve: true` to merge everything into one schema.
153
-
154
- ```typescript
155
- const schema = loadSchema('/path/to/schema.xsd', { autoLink: true });
156
- ```
157
-
158
- ### Infer Module
159
-
160
- ```typescript
161
- import type { InferSchema, InferElement, SchemaLike } from '@abapify/ts-xsd';
162
- ```
163
-
164
- #### `InferSchema<T>`
165
-
166
- Infer a TypeScript type from a schema literal (`as const`). Returns a union of all root element types.
167
-
168
- #### `InferElement<T, Name>`
169
-
170
- Infer the type for a specific root element by name.
171
-
172
- ```typescript
173
- type Person = InferElement<typeof schema, 'person'>;
174
- ```
175
-
176
- #### Built-in Type Mapping
177
-
178
- | XSD Type | TypeScript |
179
- | ------------------------------------ | ---------- |
180
- | `xs:string`, `xs:token`, `xs:NCName` | `string` |
181
- | `xs:int`, `xs:integer`, `xs:decimal` | `number` |
182
- | `xs:boolean` | `boolean` |
183
- | `xs:date`, `xs:dateTime`, `xs:time` | `string` |
184
- | `xs:anyURI`, `xs:QName` | `string` |
185
- | `xs:anyType` | `unknown` |
186
-
187
- ### XML Module
188
-
189
- ```typescript
190
- import { parseXml, buildXml } from '@abapify/ts-xsd';
191
- ```
192
-
193
- #### `parseXml<T>(schema, xml: string): T`
194
-
195
- Parse an XML string into a typed object using a schema definition.
196
-
197
- #### `buildXml<T>(schema, data: T, options?: XmlBuildOptions): string`
198
-
199
- Build an XML string from a typed object using a schema definition. Defaults to `xmlDecl: true` (includes `<?xml ...?>` declaration); pass `{ xmlDecl: false }` for bare output.
200
-
201
- ### Codegen Module
202
-
203
- ```typescript
204
- import { generateSchemaLiteral, generateInterfaces } from '@abapify/ts-xsd';
205
- ```
206
-
207
- #### `generateSchemaLiteral(xsd: string, options?): string`
208
-
209
- Generate a TypeScript `as const` schema literal from XSD content — for use with `InferSchema<T>`.
210
-
211
- ```typescript
212
- const code = generateSchemaLiteral(xsdContent, {
213
- name: 'PersonSchema',
214
- features: { $xmlns: true, $filename: true },
215
- exclude: ['annotation'],
216
- });
217
- // export const PersonSchema = { ... } as const;
218
- ```
219
-
220
- #### `generateInterfaces(schema: Schema, options?): { code: string }`
221
-
222
- Generate TypeScript interfaces from a parsed schema. Useful when schemas are too complex for compile-time `InferSchema<T>` (TypeScript TS2589 recursion limits).
223
-
224
- ```typescript
225
- const { code } = generateInterfaces(schema, {
226
- flatten: true, // Inline all nested types (default: false)
227
- addJsDoc: true, // JSDoc comments
228
- rootTypeName: 'MySchema',
229
- });
230
- ```
231
-
232
- | Option | Type | Default | Description |
233
- | -------------- | --------- | ------- | -------------------------------- |
234
- | `flatten` | `boolean` | `false` | Inline all nested types into one |
235
- | `addJsDoc` | `boolean` | `false` | Add JSDoc comments |
236
- | `rootTypeName` | `string` | auto | Custom name for the root type |
237
-
238
- ### CLI
239
-
240
- The package ships a CLI for code generation:
241
-
242
- ```bash
243
- # Config-based (recommended) — uses ts-xsd.config.ts in cwd
244
- npx ts-xsd codegen
245
-
246
- # Single-file mode
247
- npx ts-xsd codegen person.xsd
248
- npx ts-xsd codegen person.xsd ./generated/person-schema.ts
249
- npx ts-xsd codegen person.xsd --name=PersonSchema
250
- ```
251
-
252
- ### Config-Based Generation
253
-
254
- For multi-schema projects, use the composable generator system:
255
-
256
- ```typescript
257
- import {
258
- defineConfig,
259
- rawSchema,
260
- interfaces,
261
- } from '@abapify/ts-xsd/generators';
262
-
263
- export default defineConfig({
264
- sources: {
265
- base: {
266
- xsdDir: 'schemas',
267
- outputDir: 'src/generated',
268
- schemas: ['base'],
269
- },
270
- orders: {
271
- xsdDir: 'schemas',
272
- outputDir: 'src/generated',
273
- schemas: ['orders'],
274
- autoLink: true,
275
- },
276
- },
277
- generators: [rawSchema(), interfaces({ flatten: true })],
278
- });
279
- ```
280
-
281
- ## Schema Structure
282
-
283
- The `Schema` type is a 1:1 TypeScript representation of W3C XSD. Non-W3C extension properties are prefixed with `$`:
284
-
285
- ```typescript
286
- interface Schema {
287
- targetNamespace?: string;
288
- elementFormDefault?: 'qualified' | 'unqualified';
289
-
290
- // Composition
291
- import?: Import[];
292
- include?: Include[];
293
-
294
- // Declarations
295
- element?: TopLevelElement[];
296
- complexType?: TopLevelComplexType[];
297
- simpleType?: TopLevelSimpleType[];
298
- group?: NamedGroup[];
299
- attributeGroup?: NamedAttributeGroup[];
300
-
301
- // Extensions (non-W3C, prefixed with $)
302
- $xmlns?: { [prefix: string]: string }; // Namespace prefix mappings
303
- $imports?: Schema[]; // Linked imported schemas
304
- $filename?: string; // Source filename (for round-trip)
305
- }
306
- ```
307
-
308
- ### Cross-Schema Type Resolution
309
-
310
- Link schemas via `$imports` to resolve types across schema boundaries:
311
-
312
- ```typescript
313
- const base = { ...parseXsd(baseXsd), $filename: 'base.xsd' };
314
- const orders = { ...parseXsd(ordersXsd), $filename: 'orders.xsd' };
315
-
316
- const linked = resolveImports(orders, [base]);
317
-
318
- // InferSchema can now resolve types from `base`
319
- type OrderData = InferSchema<typeof linked>;
19
+ const schema = parseXsd(xsdString); // typed W3C XSD model
20
+ const xsd = buildXsd(schema);
320
21
  ```
321
22
 
322
- ## Design Principles
23
+ ## Features
323
24
 
324
- 1. **Pure W3C XSD 1.1** — types match the official [XMLSchema.xsd](https://www.w3.org/TR/xmlschema11-1/XMLSchema.xsd) exactly; no invented properties.
325
- 2. **Type-safe** — full TypeScript support with compile-time inference.
326
- 3. **Minimal dependencies** — `@xmldom/xmldom` for DOM parsing, `ts-morph` and `zod` for codegen.
327
- 4. **Tree-shakeable** — import only what you need.
328
- 5. **Round-trip verified** — tested against the official W3C XMLSchema.xsd.
25
+ - W3C XSD 1.1 parser and builder
26
+ - TypeScript type inference from `as const` schemas
27
+ - Codegen CLI (`ts-xsd`) for schema literals and interfaces
329
28
 
330
- ## References
29
+ ## Documentation
331
30
 
332
- - [W3C XML Schema 1.1 Part 1: Structures](https://www.w3.org/TR/xmlschema11-1/)
333
- - [XMLSchema.xsd](https://www.w3.org/TR/xmlschema11-1/XMLSchema.xsd)
334
- - [Codegen Guide](https://github.com/abapify/adt-cli/tree/main/packages/ts-xsd/docs/codegen.md)
31
+ **[adt-cli.netlify.app/sdk/packages/ts-xsd](https://adt-cli.netlify.app/sdk/packages/ts-xsd)**
335
32
 
336
- ## License
33
+ ---
337
34
 
338
- [MIT](https://github.com/abapify/adt-cli/blob/main/LICENSE)
35
+ Part of [**abapify**](https://github.com/abapify/adt-cli) — open, typed tooling for SAP ABAP Development Tools (ADT): CLI, MCP server, and SDK.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as runCodegen } from "../runner-BGNA8Sl-.mjs";
3
- import { t as generateSchemaFile } from "../generate-DQJ38rjv.mjs";
2
+ import { t as runCodegen } from "../runner-BZNOc_YZ.mjs";
3
+ import { t as generateSchemaFile } from "../generate-BWLnwHUW.mjs";
4
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { basename, dirname, resolve } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
@@ -127,10 +127,12 @@ async function main() {
127
127
  if (options.mode === "config") await runConfigMode(options);
128
128
  else runSingleFileMode(options);
129
129
  }
130
- main().catch((err) => {
130
+ try {
131
+ await main();
132
+ } catch (err) {
131
133
  console.error("Fatal error:", err);
132
134
  process.exit(1);
133
- });
135
+ }
134
136
 
135
137
  export { };
136
138
  //# sourceMappingURL=cli.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/codegen/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * ts-xsd Codegen CLI\n *\n * Usage:\n * ts-xsd codegen [--config=ts-xsd.config.ts] [--verbose]\n * ts-xsd codegen <input.xsd> [output.ts] [--name=SchemaName]\n *\n * Config-based (recommended):\n * ts-xsd codegen # Uses ts-xsd.config.ts in cwd\n * ts-xsd codegen --config=my-config.ts # Uses custom config\n * ts-xsd codegen --verbose # Verbose output\n *\n * Single-file (legacy):\n * ts-xsd codegen person.xsd\n * ts-xsd codegen person.xsd ./generated/person-schema.ts\n * ts-xsd codegen person.xsd --name=PersonSchema\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname, basename, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { generateSchemaFile } from './generate';\nimport { runCodegen } from './runner';\nimport type { CodegenConfig } from './types';\n\ninterface ConfigModeOptions {\n mode: 'config';\n config: string;\n verbose?: boolean;\n}\n\ninterface SingleFileModeOptions {\n mode: 'single';\n input: string;\n output: string;\n name: string;\n verbose?: boolean;\n}\n\ntype CliOptions = ConfigModeOptions | SingleFileModeOptions;\n\nfunction parseArgs(args: string[]): CliOptions {\n const positional: string[] = [];\n let config: string | undefined;\n let verbose = false;\n let name: string | undefined;\n\n for (const arg of args) {\n if (arg.startsWith('--config=')) {\n config = arg.slice(9);\n } else if (arg === '--verbose' || arg === '-v') {\n verbose = true;\n } else if (arg.startsWith('--name=')) {\n name = arg.slice(7);\n } else if (!arg.startsWith('-')) {\n positional.push(arg);\n }\n }\n\n // If first positional arg is an XSD file, use single-file mode\n if (positional[0]?.endsWith('.xsd')) {\n const input = positional[0];\n const defaultOutput = input.replace(/\\.xsd$/i, '-schema.ts');\n const output = positional[1] || defaultOutput;\n const defaultName =\n basename(input, '.xsd')\n .replaceAll(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^./, (s) => s.toLowerCase()) + 'Schema';\n\n return {\n mode: 'single',\n input: resolve(input),\n output: resolve(output),\n name: name || defaultName,\n verbose,\n };\n }\n\n // Config-based mode\n return {\n mode: 'config',\n config: config || 'ts-xsd.config.ts',\n verbose,\n };\n}\n\nasync function runConfigMode(options: ConfigModeOptions) {\n const configPath = resolve(options.config);\n\n if (!existsSync(configPath)) {\n console.error(`❌ Config file not found: ${configPath}`);\n console.error(\n '\\nCreate a ts-xsd.config.ts file or specify --config=path/to/config.ts',\n );\n process.exit(1);\n }\n\n console.log(`📦 Loading config: ${configPath}\\n`);\n\n // Dynamic import of config file\n const configUrl = pathToFileURL(configPath).href;\n const configModule = await import(configUrl);\n const config: CodegenConfig = configModule.default;\n\n const result = await runCodegen(config, {\n rootDir: dirname(configPath),\n verbose: options.verbose,\n });\n\n console.log(`\\n📊 Summary:`);\n console.log(` - Schemas processed: ${result.schemas.length}`);\n console.log(` - Files generated: ${result.files.length}`);\n console.log(` - Errors: ${result.errors.length}`);\n\n if (result.errors.length > 0) {\n console.error('\\n❌ Errors:');\n for (const err of result.errors) {\n console.error(` - ${err.schema || 'unknown'}: ${err.error.message}`);\n }\n process.exit(1);\n }\n\n console.log('\\n✅ Codegen complete!');\n}\n\nfunction runSingleFileMode(options: SingleFileModeOptions) {\n console.log(`Reading: ${options.input}`);\n const xsdContent = readFileSync(options.input, 'utf-8');\n\n console.log(`Generating schema: ${options.name}`);\n const tsContent = generateSchemaFile(xsdContent, {\n name: options.name,\n comment: `Source: ${basename(options.input)}`,\n });\n\n mkdirSync(dirname(options.output), { recursive: true });\n\n console.log(`Writing: ${options.output}`);\n writeFileSync(options.output, tsContent);\n\n console.log(`Done! Generated ${tsContent.length} characters`);\n console.log(`\\nUsage in TypeScript:`);\n console.log(\n ` import { ${options.name} } from './${basename(options.output, '.ts')}';`,\n );\n console.log(` import type { InferSchema } from '@abapify/ts-xsd';`);\n console.log(` type MyType = InferSchema<typeof ${options.name}>;`);\n}\n\nfunction showHelp() {\n console.log(`\nts-xsd Codegen CLI\n\nUsage:\n ts-xsd codegen [options] Config-based generation (recommended)\n ts-xsd codegen <input.xsd> [output] Single-file generation (legacy)\n\nOptions:\n --config=FILE Config file path (default: ts-xsd.config.ts)\n --verbose, -v Verbose output\n --name=NAME Schema variable name (single-file mode only)\n --help, -h Show this help\n\nExamples:\n # Config-based (recommended)\n ts-xsd codegen # Uses ts-xsd.config.ts\n ts-xsd codegen --config=my-config.ts # Custom config\n ts-xsd codegen --verbose # With verbose output\n\n # Single-file (legacy)\n ts-xsd codegen person.xsd\n ts-xsd codegen person.xsd ./out/person.ts --name=PersonSchema\n`);\n}\n\nasync function main() {\n const args = process.argv.slice(2);\n\n if (args.includes('--help') || args.includes('-h')) {\n showHelp();\n process.exit(0);\n }\n\n const options = parseArgs(args);\n\n if (options.mode === 'config') {\n await runConfigMode(options);\n } else {\n runSingleFileMode(options);\n }\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/codegen/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * ts-xsd Codegen CLI\n *\n * Usage:\n * ts-xsd codegen [--config=ts-xsd.config.ts] [--verbose]\n * ts-xsd codegen <input.xsd> [output.ts] [--name=SchemaName]\n *\n * Config-based (recommended):\n * ts-xsd codegen # Uses ts-xsd.config.ts in cwd\n * ts-xsd codegen --config=my-config.ts # Uses custom config\n * ts-xsd codegen --verbose # Verbose output\n *\n * Single-file (legacy):\n * ts-xsd codegen person.xsd\n * ts-xsd codegen person.xsd ./generated/person-schema.ts\n * ts-xsd codegen person.xsd --name=PersonSchema\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname, basename, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { generateSchemaFile } from './generate';\nimport { runCodegen } from './runner';\nimport type { CodegenConfig } from './types';\n\ninterface ConfigModeOptions {\n mode: 'config';\n config: string;\n verbose?: boolean;\n}\n\ninterface SingleFileModeOptions {\n mode: 'single';\n input: string;\n output: string;\n name: string;\n verbose?: boolean;\n}\n\ntype CliOptions = ConfigModeOptions | SingleFileModeOptions;\n\nfunction parseArgs(args: string[]): CliOptions {\n const positional: string[] = [];\n let config: string | undefined;\n let verbose = false;\n let name: string | undefined;\n\n for (const arg of args) {\n if (arg.startsWith('--config=')) {\n config = arg.slice(9);\n } else if (arg === '--verbose' || arg === '-v') {\n verbose = true;\n } else if (arg.startsWith('--name=')) {\n name = arg.slice(7);\n } else if (!arg.startsWith('-')) {\n positional.push(arg);\n }\n }\n\n // If first positional arg is an XSD file, use single-file mode\n if (positional[0]?.endsWith('.xsd')) {\n const input = positional[0];\n const defaultOutput = input.replace(/\\.xsd$/i, '-schema.ts');\n const output = positional[1] || defaultOutput;\n const defaultName =\n basename(input, '.xsd')\n .replaceAll(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^./, (s) => s.toLowerCase()) + 'Schema';\n\n return {\n mode: 'single',\n input: resolve(input),\n output: resolve(output),\n name: name || defaultName,\n verbose,\n };\n }\n\n // Config-based mode\n return {\n mode: 'config',\n config: config || 'ts-xsd.config.ts',\n verbose,\n };\n}\n\nasync function runConfigMode(options: ConfigModeOptions) {\n const configPath = resolve(options.config);\n\n if (!existsSync(configPath)) {\n console.error(`❌ Config file not found: ${configPath}`);\n console.error(\n '\\nCreate a ts-xsd.config.ts file or specify --config=path/to/config.ts',\n );\n process.exit(1);\n }\n\n console.log(`📦 Loading config: ${configPath}\\n`);\n\n // Dynamic import of config file\n const configUrl = pathToFileURL(configPath).href;\n const configModule = await import(configUrl);\n const config: CodegenConfig = configModule.default;\n\n const result = await runCodegen(config, {\n rootDir: dirname(configPath),\n verbose: options.verbose,\n });\n\n console.log(`\\n📊 Summary:`);\n console.log(` - Schemas processed: ${result.schemas.length}`);\n console.log(` - Files generated: ${result.files.length}`);\n console.log(` - Errors: ${result.errors.length}`);\n\n if (result.errors.length > 0) {\n console.error('\\n❌ Errors:');\n for (const err of result.errors) {\n console.error(` - ${err.schema || 'unknown'}: ${err.error.message}`);\n }\n process.exit(1);\n }\n\n console.log('\\n✅ Codegen complete!');\n}\n\nfunction runSingleFileMode(options: SingleFileModeOptions) {\n console.log(`Reading: ${options.input}`);\n const xsdContent = readFileSync(options.input, 'utf-8');\n\n console.log(`Generating schema: ${options.name}`);\n const tsContent = generateSchemaFile(xsdContent, {\n name: options.name,\n comment: `Source: ${basename(options.input)}`,\n });\n\n mkdirSync(dirname(options.output), { recursive: true });\n\n console.log(`Writing: ${options.output}`);\n writeFileSync(options.output, tsContent);\n\n console.log(`Done! Generated ${tsContent.length} characters`);\n console.log(`\\nUsage in TypeScript:`);\n console.log(\n ` import { ${options.name} } from './${basename(options.output, '.ts')}';`,\n );\n console.log(` import type { InferSchema } from '@abapify/ts-xsd';`);\n console.log(` type MyType = InferSchema<typeof ${options.name}>;`);\n}\n\nfunction showHelp() {\n console.log(`\nts-xsd Codegen CLI\n\nUsage:\n ts-xsd codegen [options] Config-based generation (recommended)\n ts-xsd codegen <input.xsd> [output] Single-file generation (legacy)\n\nOptions:\n --config=FILE Config file path (default: ts-xsd.config.ts)\n --verbose, -v Verbose output\n --name=NAME Schema variable name (single-file mode only)\n --help, -h Show this help\n\nExamples:\n # Config-based (recommended)\n ts-xsd codegen # Uses ts-xsd.config.ts\n ts-xsd codegen --config=my-config.ts # Custom config\n ts-xsd codegen --verbose # With verbose output\n\n # Single-file (legacy)\n ts-xsd codegen person.xsd\n ts-xsd codegen person.xsd ./out/person.ts --name=PersonSchema\n`);\n}\n\nasync function main() {\n const args = process.argv.slice(2);\n\n if (args.includes('--help') || args.includes('-h')) {\n showHelp();\n process.exit(0);\n }\n\n const options = parseArgs(args);\n\n if (options.mode === 'config') {\n await runConfigMode(options);\n } else {\n runSingleFileMode(options);\n }\n}\n\ntry {\n await main();\n} catch (err) {\n console.error('Fatal error:', err);\n process.exit(1);\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { p as parseXsd } from "./runner-BGNA8Sl-.mjs";
1
+ import { p as parseXsd } from "./runner-BZNOc_YZ.mjs";
2
2
 
3
3
  // src/codegen/generate.ts
4
4
  /**
@@ -148,7 +148,7 @@ function schemaToLiteral(schema, options = {}) {
148
148
  * and escaping any single quotes.
149
149
  */
150
150
  function toSingleQuoteLiteral(jsonStr) {
151
- return `'${jsonStr.slice(1, -1).replaceAll("\\\"", "\"").replaceAll("'", "\\'")}'`;
151
+ return `'${jsonStr.slice(1, -1).replaceAll(String.raw`\"`, "\"").replaceAll("'", String.raw`\'`)}'`;
152
152
  }
153
153
  /**
154
154
  * Convert any value to a TypeScript literal string.
@@ -158,40 +158,45 @@ function objectToLiteral(value, pretty, indent, depth, singleQuote = false) {
158
158
  if (value instanceof SchemaRef) return value.name;
159
159
  if (typeof value === "string") {
160
160
  const json = JSON.stringify(value);
161
- if (singleQuote) return toSingleQuoteLiteral(json);
162
- return json;
161
+ return singleQuote ? toSingleQuoteLiteral(json) : json;
163
162
  }
164
163
  if (typeof value === "number" || typeof value === "boolean") return String(value);
165
- if (Array.isArray(value)) {
166
- if (value.length === 0) return "[]";
167
- const items = value.map((item) => objectToLiteral(item, pretty, indent, depth + 1, singleQuote));
168
- if (pretty) {
169
- const itemIndent = indent.repeat(depth + 1);
170
- const closeIndent = indent.repeat(depth);
171
- return `[\n${items.map((item) => `${itemIndent}${item}`).join(",\n")},\n${closeIndent}]`;
172
- }
173
- return `[${items.join(", ")}]`;
164
+ if (Array.isArray(value)) return arrayToLiteral(value, pretty, indent, depth, singleQuote);
165
+ if (typeof value === "object") return recordToLiteral(value, pretty, indent, depth, singleQuote);
166
+ return "undefined";
167
+ }
168
+ /** Render an array literal. */
169
+ function arrayToLiteral(value, pretty, indent, depth, singleQuote) {
170
+ if (value.length === 0) return "[]";
171
+ const items = value.map((item) => objectToLiteral(item, pretty, indent, depth + 1, singleQuote));
172
+ if (pretty) {
173
+ const itemIndent = indent.repeat(depth + 1);
174
+ const closeIndent = indent.repeat(depth);
175
+ return `[\n${items.map((item) => `${itemIndent}${item}`).join(",\n")},\n${closeIndent}]`;
174
176
  }
175
- if (typeof value === "object") {
176
- const entries = Object.entries(value).filter(([, v]) => v !== void 0);
177
- if (entries.length === 0) return "{}";
178
- const props = entries.map(([key, val]) => {
179
- let keyStr = key;
180
- if (!isValidIdentifier(key)) {
181
- const json = JSON.stringify(key);
182
- keyStr = singleQuote ? toSingleQuoteLiteral(json) : json;
183
- }
184
- const valStr = objectToLiteral(val, pretty, indent, depth + 1, singleQuote);
185
- return `${keyStr}: ${valStr}`;
186
- });
187
- if (pretty) {
188
- const propIndent = indent.repeat(depth + 1);
189
- const closeIndent = indent.repeat(depth);
190
- return `{\n${props.map((prop) => `${propIndent}${prop}`).join(",\n")},\n${closeIndent}}`;
191
- }
192
- return `{ ${props.join(", ")} }`;
177
+ return `[${items.join(", ")}]`;
178
+ }
179
+ /** Render an object literal from record entries. */
180
+ function recordToLiteral(obj, pretty, indent, depth, singleQuote) {
181
+ const entries = Object.entries(obj).filter(([, v]) => v !== void 0);
182
+ if (entries.length === 0) return "{}";
183
+ const props = entries.map(([key, val]) => propertyToLiteral(key, val, pretty, indent, depth, singleQuote));
184
+ if (pretty) {
185
+ const propIndent = indent.repeat(depth + 1);
186
+ const closeIndent = indent.repeat(depth);
187
+ return `{\n${props.map((prop) => `${propIndent}${prop}`).join(",\n")},\n${closeIndent}}`;
193
188
  }
194
- return "undefined";
189
+ return `{ ${props.join(", ")} }`;
190
+ }
191
+ /** Render one `key: value` object property. */
192
+ function propertyToLiteral(key, val, pretty, indent, depth, singleQuote) {
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}`;
195
200
  }
196
201
  /**
197
202
  * JavaScript reserved words that cannot be used as variable names.
@@ -258,4 +263,4 @@ function pascalCase(str) {
258
263
  }
259
264
 
260
265
  export { generateSchemaLiteral as n, generateSchemaFile as t };
261
- //# sourceMappingURL=generate-DQJ38rjv.mjs.map
266
+ //# sourceMappingURL=generate-BWLnwHUW.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-BWLnwHUW.mjs","names":[],"sources":["../src/codegen/generate.ts"],"sourcesContent":["/**\n * Schema Literal Generator\n *\n * Transforms a parsed Schema object into a TypeScript literal string\n * that preserves type information for InferSchema<T>.\n */\n\nimport { parseXsd } from '../xsd';\nimport type { Schema } from '../xsd/types';\n\nexport interface GenerateOptions {\n /** Variable name for the exported schema */\n name?: string;\n /** Add JSDoc comment */\n comment?: string;\n /** Pretty print with indentation */\n pretty?: boolean;\n /** Indentation string (default: ' ') */\n indent?: string;\n /**\n * Enable specific $ metadata features in output.\n * - `$xmlns`: Keep `$xmlns` in output (already parsed as $xmlns)\n * - `$imports`: Rename `import` → `$imports` for schema linking\n * - `$filename`: Include `$filename` (uses `name` option + '.xsd')\n *\n * @example\n * ```typescript\n * features: { $xmlns: true, $imports: true, $filename: true }\n * ```\n */\n features?: {\n /** Keep `$xmlns` in output */\n $xmlns?: boolean;\n /** Rename `import` → `$imports` for schema linking */\n $imports?: boolean;\n /** Include `$filename` (uses `name` + '.xsd') */\n $filename?: boolean;\n };\n /**\n * Property names to exclude from output.\n * Useful for removing annotations, documentation, etc.\n *\n * @example\n * ```typescript\n * exclude: ['annotation'] // Remove all annotation elements\n * ```\n */\n exclude?: string[];\n /** Resolver for import paths (schemaLocation -> module path) */\n importResolver?: (schemaLocation: string) => string | null;\n /**\n * Generate isolatedDeclarations-compatible output.\n * Uses `satisfies Schema as const` instead of just `as const`.\n */\n isolatedDeclarations?: boolean;\n /** Use single quotes for string literals and property names */\n singleQuote?: boolean;\n}\n\n/**\n * Generate a TypeScript literal string from XSD content.\n *\n * @param xsdContent - XSD file content as string\n * @param options - Generation options\n * @returns TypeScript code with schema as const literal\n *\n * @example\n * ```typescript\n * const xsd = `<xs:schema>...</xs:schema>`;\n * const code = generateSchemaLiteral(xsd, { name: 'PersonSchema' });\n * // export const PersonSchema = { ... } as const;\n * ```\n */\nexport function generateSchemaLiteral(\n xsdContent: string,\n options: GenerateOptions = {},\n): string {\n const schema = parseXsd(xsdContent);\n const features = options.features ?? {};\n const exclude = new Set(options.exclude ?? []);\n\n // Transform schema\n let outputSchema: Record<string, unknown> = { ...schema };\n\n // Add $filename when enabled (uses name option)\n if (features.$filename && options.name) {\n outputSchema = { $filename: `${options.name}.xsd`, ...outputSchema };\n }\n\n // Apply transformations (features + exclude)\n outputSchema = applyTransforms(outputSchema, features, exclude);\n\n return schemaToLiteral(outputSchema as Schema, options);\n}\n\n/**\n * Apply transformations: features ($xmlns, $imports) and exclude filter.\n * Used by generateSchemaLiteral (without imports).\n */\nfunction applyTransforms(\n schema: Record<string, unknown>,\n features: NonNullable<GenerateOptions['features']>,\n exclude: Set<string>,\n): Record<string, unknown> {\n return applyTransformsWithImports(schema, features, exclude, []);\n}\n\n/**\n * Special marker for schema references (not serialized as strings).\n */\nclass SchemaRef {\n constructor(public readonly name: string) {}\n}\n\n/**\n * Apply transformations with schema imports as references.\n */\nfunction applyTransformsWithImports(\n schema: Record<string, unknown>,\n features: NonNullable<GenerateOptions['features']>,\n exclude: Set<string>,\n importedSchemaNames: string[],\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(schema)) {\n // Skip excluded properties\n if (exclude.has(key)) {\n continue;\n }\n\n if (key === '$xmlns') {\n // $xmlns is already parsed - keep only if feature enabled\n if (features.$xmlns) {\n result[key] = value;\n }\n } else if (key === 'import') {\n // Convert import to $imports with schema references\n if (features.$imports && importedSchemaNames.length > 0) {\n result['$imports'] = importedSchemaNames.map(\n (name) => new SchemaRef(name),\n );\n }\n // If no imports resolved, skip the import property entirely\n } else {\n // Recursively filter nested objects/arrays for exclude\n result[key] = filterDeep(value, exclude);\n }\n }\n\n return result;\n}\n\n/**\n * Recursively filter excluded properties from nested structures.\n */\nfunction filterDeep(value: unknown, exclude: Set<string>): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => filterDeep(item, exclude));\n }\n\n if (value !== null && typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n if (!exclude.has(k)) {\n result[k] = filterDeep(v, exclude);\n }\n }\n return result;\n }\n\n return value;\n}\n\n/**\n * Generate a complete TypeScript file from XSD content.\n *\n * @param xsdContent - XSD file content as string\n * @param options - Generation options\n * @returns Complete TypeScript file content\n */\nexport function generateSchemaFile(\n xsdContent: string,\n options: GenerateOptions = {},\n): string {\n const {\n name = 'schema',\n comment,\n features = {},\n importResolver,\n isolatedDeclarations = false,\n } = options;\n\n // Parse to extract imports\n const schema = parseXsd(xsdContent);\n const xsdImports = (schema.import ?? []) as Array<{\n schemaLocation?: string;\n namespace?: string;\n }>;\n\n // Generate TypeScript import statements if $imports feature enabled\n const tsImports: string[] = [];\n const importedSchemaNames: string[] = [];\n\n // Add Schema type import for isolatedDeclarations mode\n if (isolatedDeclarations) {\n tsImports.push(\"import type { Schema } from '@abapify/ts-xsd';\");\n }\n\n if (features.$imports && importResolver) {\n for (const imp of xsdImports) {\n if (imp.schemaLocation) {\n const modulePath = importResolver(imp.schemaLocation);\n if (modulePath) {\n // Derive schema name from schemaLocation basename (e.g., \"../sap/adtcore.xsd\" → \"adtcore\")\n const schemaName = imp.schemaLocation\n .replace(/\\.xsd$/, '')\n .replace(/^.*\\//, '');\n tsImports.push(`import ${schemaName} from '${modulePath}';`);\n importedSchemaNames.push(schemaName);\n }\n }\n }\n }\n\n const lines: string[] = [\n '/**',\n ' * Auto-generated schema literal from XSD',\n ' * ',\n ' * DO NOT EDIT - Generated by ts-xsd codegen',\n comment ? ` * ${comment}` : null,\n ' */',\n '',\n // Add TypeScript imports\n ...tsImports,\n tsImports.length > 0 ? '' : null,\n // Generate schema with $imports as array of schema references\n generateSchemaLiteralWithImports(xsdContent, options, importedSchemaNames),\n '',\n `export type ${pascalCase(name)}Type = typeof ${name};`,\n '',\n ].filter((line): line is string => line !== null);\n\n return lines.join('\\n');\n}\n\n/**\n * Generate schema literal with $imports as schema references.\n */\nfunction generateSchemaLiteralWithImports(\n xsdContent: string,\n options: GenerateOptions,\n importedSchemaNames: string[],\n): string {\n const schema = parseXsd(xsdContent);\n const features = options.features ?? {};\n const exclude = new Set(options.exclude ?? []);\n\n // Transform schema\n let outputSchema: Record<string, unknown> = { ...schema };\n\n // Add $filename when enabled (uses name option)\n if (features.$filename && options.name) {\n outputSchema = { $filename: `${options.name}.xsd`, ...outputSchema };\n }\n\n // Apply transformations (features + exclude), passing imported schema names\n outputSchema = applyTransformsWithImports(\n outputSchema,\n features,\n exclude,\n importedSchemaNames,\n );\n\n return schemaToLiteral(outputSchema as Schema, options);\n}\n\n/**\n * Escape reserved words by adding underscore suffix.\n */\nfunction escapeReservedWord(name: string): string {\n return RESERVED_WORDS.has(name) ? `${name}_` : name;\n}\n\n/**\n * Convert a Schema object to a TypeScript literal string.\n */\nfunction schemaToLiteral(\n schema: Schema,\n options: GenerateOptions = {},\n): string {\n const {\n name = 'schema',\n pretty = true,\n indent = ' ',\n isolatedDeclarations = false,\n singleQuote = false,\n } = options;\n\n const safeName = escapeReservedWord(name);\n const literal = objectToLiteral(schema, pretty, indent, 0, singleQuote);\n\n // For isolatedDeclarations compatibility, use `satisfies Schema as const`\n // This makes the type explicit so TypeScript can emit declarations\n const typeAssertion = isolatedDeclarations\n ? 'satisfies Schema as const'\n : 'as const';\n\n return `export const ${safeName} = ${literal} ${typeAssertion};`;\n}\n\n/**\n * Format a string value as a single-quoted literal, unescaping JSON double quotes\n * and escaping any single quotes.\n */\nfunction toSingleQuoteLiteral(jsonStr: string): string {\n const inner = jsonStr\n .slice(1, -1)\n .replaceAll(String.raw`\\\"`, '\"')\n .replaceAll(\"'\", String.raw`\\'`);\n return `'${inner}'`;\n}\n\n/**\n * Convert any value to a TypeScript literal string.\n */\nfunction objectToLiteral(\n value: unknown,\n pretty: boolean,\n indent: string,\n depth: number,\n singleQuote = false,\n): string {\n if (value === null || value === undefined) {\n return 'undefined';\n }\n\n // Handle SchemaRef - output as bare identifier (not quoted)\n if (value instanceof SchemaRef) {\n return value.name;\n }\n\n if (typeof value === 'string') {\n const json = JSON.stringify(value);\n return singleQuote ? toSingleQuoteLiteral(json) : json;\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n\n if (Array.isArray(value)) {\n return arrayToLiteral(value, pretty, indent, depth, singleQuote);\n }\n\n if (typeof value === 'object') {\n return recordToLiteral(\n value as Record<string, unknown>,\n pretty,\n indent,\n depth,\n singleQuote,\n );\n }\n\n return 'undefined';\n}\n\n/** Render an array literal. */\nfunction arrayToLiteral(\n value: unknown[],\n pretty: boolean,\n indent: string,\n depth: number,\n singleQuote: boolean,\n): string {\n if (value.length === 0) {\n return '[]';\n }\n\n const items = value.map((item) =>\n objectToLiteral(item, pretty, indent, depth + 1, singleQuote),\n );\n\n if (pretty) {\n const itemIndent = indent.repeat(depth + 1);\n const closeIndent = indent.repeat(depth);\n return `[\\n${items\n .map((item) => `${itemIndent}${item}`)\n .join(',\\n')},\\n${closeIndent}]`;\n }\n\n return `[${items.join(', ')}]`;\n}\n\n/** Render an object literal from record entries. */\nfunction recordToLiteral(\n obj: Record<string, unknown>,\n pretty: boolean,\n indent: string,\n depth: number,\n singleQuote: boolean,\n): string {\n const entries = Object.entries(obj).filter(([, v]) => v !== undefined);\n\n if (entries.length === 0) {\n return '{}';\n }\n\n const props = entries.map(([key, val]) =>\n propertyToLiteral(key, val, pretty, indent, depth, singleQuote),\n );\n\n if (pretty) {\n const propIndent = indent.repeat(depth + 1);\n const closeIndent = indent.repeat(depth);\n return `{\\n${props\n .map((prop) => `${propIndent}${prop}`)\n .join(',\\n')},\\n${closeIndent}}`;\n }\n\n return `{ ${props.join(', ')} }`;\n}\n\n/** Render one `key: value` object property. */\nfunction propertyToLiteral(\n key: string,\n val: unknown,\n pretty: boolean,\n indent: string,\n depth: number,\n singleQuote: boolean,\n): string {\n let keyStr = key;\n if (!isValidIdentifier(key)) {\n const json = JSON.stringify(key);\n keyStr = singleQuote ? toSingleQuoteLiteral(json) : json;\n }\n const valStr = objectToLiteral(val, pretty, indent, depth + 1, singleQuote);\n return `${keyStr}: ${valStr}`;\n}\n\n/**\n * JavaScript reserved words that cannot be used as variable names.\n */\nconst RESERVED_WORDS = new Set([\n 'break',\n 'case',\n 'catch',\n 'continue',\n 'debugger',\n 'default',\n 'delete',\n 'do',\n 'else',\n 'finally',\n 'for',\n 'function',\n 'if',\n 'in',\n 'instanceof',\n 'new',\n 'return',\n 'switch',\n 'this',\n 'throw',\n 'try',\n 'typeof',\n 'var',\n 'void',\n 'while',\n 'with',\n 'class',\n 'const',\n 'enum',\n 'export',\n 'extends',\n 'import',\n 'super',\n 'implements',\n 'interface',\n 'let',\n 'package',\n 'private',\n 'protected',\n 'public',\n 'static',\n 'yield',\n 'await',\n 'null',\n 'true',\n 'false',\n]);\n\n/**\n * Check if a string is a valid JavaScript identifier.\n */\nfunction isValidIdentifier(str: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str) && !RESERVED_WORDS.has(str);\n}\n\n/**\n * Convert string to PascalCase.\n */\nfunction pascalCase(str: string): string {\n return str\n .replaceAll(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^./, (s) => s.toUpperCase());\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { _ as TransformContext, c as CodegenConfig, d as GeneratorPlugin, f as HookContext, g as SourceInfo, h as SourceConfig, i as ResolveOptions, l as FinalizeContext, m as SetupContext, n as RunnerResult, p as SchemaInfo, r as runCodegen, s as AfterAllContext, t as RunnerOptions, u as GeneratedFile, v as defineConfig } from "../runner-CDEKB5AE.mjs";
1
+ import { _ as TransformContext, c as CodegenConfig, d as GeneratorPlugin, f as HookContext, g as SourceInfo, h as SourceConfig, i as ResolveOptions, l as FinalizeContext, m as SetupContext, n as RunnerResult, p as SchemaInfo, r as runCodegen, s as AfterAllContext, t as RunnerOptions, u as GeneratedFile, v as defineConfig } from "../runner-x86T9IvM.mjs";
2
2
  interface RawSchemaOptions {
3
3
  /** Use default export (default: true) */
4
4
  defaultExport?: boolean;