@abapify/ts-xsd 0.0.0 → 0.3.4
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 +405 -2
- package/package.json +36 -3
package/README.md
CHANGED
|
@@ -1,3 +1,406 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ts-xsd
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/abapify/adt-cli/pkgs/npm/%40abapify%2Fts-xsd)
|
|
4
|
+
|
|
5
|
+
**Core XSD parser, builder, and type inference** with **1:1 TypeScript representation** of W3C XML Schema Definition (XSD) 1.1.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
`ts-xsd` is a comprehensive TypeScript library for working with W3C XSD schemas. It provides:
|
|
10
|
+
|
|
11
|
+
| Module | Purpose |
|
|
12
|
+
| ----------- | ------------------------------------------------------------------- |
|
|
13
|
+
| **xsd** | Parse XSD files into typed `Schema` objects, build XSD from objects |
|
|
14
|
+
| **infer** | Compile-time TypeScript type inference from schema literals |
|
|
15
|
+
| **xml** | Parse/build XML documents using schema definitions |
|
|
16
|
+
| **codegen** | Generate TypeScript schema literals from XSD files |
|
|
17
|
+
|
|
18
|
+
### Key Features
|
|
19
|
+
|
|
20
|
+
- **Pure W3C XSD 1.1** - Types match the official [XMLSchema.xsd](https://www.w3.org/TR/xmlschema11-1/XMLSchema.xsd) exactly
|
|
21
|
+
- **Full roundtrip** - `XSD → Schema → XSD` with semantic preservation
|
|
22
|
+
- **Type inference** - `InferSchema<T>` extracts TypeScript types from schema literals
|
|
23
|
+
- **Shared types** - Cross-schema type resolution via `$imports`
|
|
24
|
+
- **Tree-shakeable** - Only import what you need
|
|
25
|
+
- **Zero runtime dependencies** - Only `@xmldom/xmldom` for DOM parsing
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @abapify/ts-xsd
|
|
31
|
+
# or
|
|
32
|
+
bun add @abapify/ts-xsd
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Parse and Build XSD
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { parseXsd, buildXsd } from '@abapify/ts-xsd';
|
|
41
|
+
|
|
42
|
+
// Parse XSD to typed Schema object
|
|
43
|
+
const schema = parseXsd(`
|
|
44
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
45
|
+
<xs:element name="person" type="PersonType"/>
|
|
46
|
+
<xs:complexType name="PersonType">
|
|
47
|
+
<xs:sequence>
|
|
48
|
+
<xs:element name="name" type="xs:string"/>
|
|
49
|
+
<xs:element name="age" type="xs:int" minOccurs="0"/>
|
|
50
|
+
</xs:sequence>
|
|
51
|
+
</xs:complexType>
|
|
52
|
+
</xs:schema>
|
|
53
|
+
`);
|
|
54
|
+
|
|
55
|
+
// Build back to XSD
|
|
56
|
+
const xsd = buildXsd(schema, { pretty: true });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Type Inference from Schema Literals
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import type { InferSchema } from '@abapify/ts-xsd';
|
|
63
|
+
|
|
64
|
+
// Define schema as const literal
|
|
65
|
+
const personSchema = {
|
|
66
|
+
element: [{ name: 'person', type: 'PersonType' }],
|
|
67
|
+
complexType: [
|
|
68
|
+
{
|
|
69
|
+
name: 'PersonType',
|
|
70
|
+
sequence: {
|
|
71
|
+
element: [
|
|
72
|
+
{ name: 'name', type: 'xs:string' },
|
|
73
|
+
{ name: 'age', type: 'xs:int', minOccurs: 0 },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
} as const;
|
|
79
|
+
|
|
80
|
+
// Infer TypeScript type at compile time
|
|
81
|
+
type Person = InferSchema<typeof personSchema>;
|
|
82
|
+
// Result: { name: string; age?: number }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Parse XML with Schema
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { parseXml, buildXml } from '@abapify/ts-xsd';
|
|
89
|
+
|
|
90
|
+
const xml = `<person><name>John</name><age>30</age></person>`;
|
|
91
|
+
const data = parseXml(personSchema, xml);
|
|
92
|
+
// data: { name: 'John', age: 30 }
|
|
93
|
+
|
|
94
|
+
const rebuilt = buildXml(personSchema, data);
|
|
95
|
+
// rebuilt: <person><name>John</name><age>30</age></person>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## API Reference
|
|
99
|
+
|
|
100
|
+
### XSD Module
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { parseXsd, buildXsd, type Schema } from '@abapify/ts-xsd';
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `parseXsd(xsd: string): Schema`
|
|
107
|
+
|
|
108
|
+
Parse an XSD XML string into a typed Schema object.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const schema = parseXsd(xsdString);
|
|
112
|
+
console.log(schema.targetNamespace);
|
|
113
|
+
console.log(schema.element?.[0].name);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### `buildXsd(schema: Schema, options?: BuildOptions): string`
|
|
117
|
+
|
|
118
|
+
Build an XSD XML string from a Schema object.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const xsd = buildXsd(schema, {
|
|
122
|
+
prefix: 'xsd', // Namespace prefix (default: 'xs')
|
|
123
|
+
pretty: true, // Pretty print (default: true)
|
|
124
|
+
indent: ' ', // Indentation (default: ' ')
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### `resolveImports(schema: Schema, resolver: (location: string) => Schema): Schema`
|
|
129
|
+
|
|
130
|
+
Resolve and link imported schemas for cross-schema type resolution.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const linkedSchema = resolveImports(schema, (location) => {
|
|
134
|
+
return parseXsd(fs.readFileSync(location, 'utf-8'));
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Infer Module
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import type { InferSchema, InferElement, SchemaLike } from '@abapify/ts-xsd';
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### `InferSchema<T>`
|
|
145
|
+
|
|
146
|
+
Infer TypeScript type from a schema literal. Returns union of all root element types.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
type Data = InferSchema<typeof mySchema>;
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### `InferElement<T, ElementName>`
|
|
153
|
+
|
|
154
|
+
Infer type for a specific element by name.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
type Person = InferElement<typeof schema, 'person'>;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### Built-in Type Mapping
|
|
161
|
+
|
|
162
|
+
| XSD Type | TypeScript |
|
|
163
|
+
| ------------------------------------ | ---------- |
|
|
164
|
+
| `xs:string`, `xs:token`, `xs:NCName` | `string` |
|
|
165
|
+
| `xs:int`, `xs:integer`, `xs:decimal` | `number` |
|
|
166
|
+
| `xs:boolean` | `boolean` |
|
|
167
|
+
| `xs:date`, `xs:dateTime`, `xs:time` | `string` |
|
|
168
|
+
| `xs:anyURI`, `xs:QName` | `string` |
|
|
169
|
+
| `xs:anyType` | `unknown` |
|
|
170
|
+
|
|
171
|
+
### XML Module
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { parseXml, buildXml } from '@abapify/ts-xsd';
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### `parseXml<T>(schema: SchemaLike, xml: string): T`
|
|
178
|
+
|
|
179
|
+
Parse XML string using schema definition.
|
|
180
|
+
|
|
181
|
+
#### `buildXml<T>(schema: SchemaLike, data: T): string`
|
|
182
|
+
|
|
183
|
+
Build XML string from data using schema definition.
|
|
184
|
+
|
|
185
|
+
### Codegen Module
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { generateSchemaLiteral, generateInterfaces } from '@abapify/ts-xsd';
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### `generateSchemaLiteral(xsd: string, options?: GenerateOptions): string`
|
|
192
|
+
|
|
193
|
+
Generate TypeScript schema literal from XSD content.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const code = generateSchemaLiteral(xsdContent, {
|
|
197
|
+
name: 'PersonSchema',
|
|
198
|
+
features: { $xmlns: true, $imports: true },
|
|
199
|
+
exclude: ['annotation'],
|
|
200
|
+
});
|
|
201
|
+
// export default { ... } as const;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### `generateInterfaces(schema: Schema, options?: GenerateInterfacesOptions): GenerateInterfacesResult`
|
|
205
|
+
|
|
206
|
+
Generate TypeScript interfaces from parsed schema. Returns an object with `code` property containing the generated TypeScript code.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const { code } = generateInterfaces(schema, {
|
|
210
|
+
flatten: true, // Inline all nested types (default: false)
|
|
211
|
+
addJsDoc: true, // Add JSDoc comments
|
|
212
|
+
rootTypeName: 'MySchema', // Custom root type name
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Schema Structure
|
|
217
|
+
|
|
218
|
+
The `Schema` type is a 1:1 TypeScript representation of W3C XSD:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
interface Schema {
|
|
222
|
+
// Namespace
|
|
223
|
+
targetNamespace?: string;
|
|
224
|
+
elementFormDefault?: 'qualified' | 'unqualified';
|
|
225
|
+
attributeFormDefault?: 'qualified' | 'unqualified';
|
|
226
|
+
|
|
227
|
+
// Composition
|
|
228
|
+
import?: Import[];
|
|
229
|
+
include?: Include[];
|
|
230
|
+
|
|
231
|
+
// Declarations
|
|
232
|
+
element?: TopLevelElement[];
|
|
233
|
+
complexType?: TopLevelComplexType[];
|
|
234
|
+
simpleType?: TopLevelSimpleType[];
|
|
235
|
+
group?: NamedGroup[];
|
|
236
|
+
attributeGroup?: NamedAttributeGroup[];
|
|
237
|
+
|
|
238
|
+
// Extensions (non-W3C, prefixed with $)
|
|
239
|
+
$xmlns?: { [prefix: string]: string };
|
|
240
|
+
$imports?: Schema[]; // Resolved imported schemas
|
|
241
|
+
$filename?: string; // Source filename
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Cross-Schema Type Resolution
|
|
246
|
+
|
|
247
|
+
Link schemas together for cross-schema type resolution:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const adtcore = parseXsd(adtcoreXsd);
|
|
251
|
+
const classes = parseXsd(classesXsd);
|
|
252
|
+
|
|
253
|
+
// Link schemas via $imports
|
|
254
|
+
const linkedClasses = {
|
|
255
|
+
...classes,
|
|
256
|
+
$imports: [adtcore],
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Now InferSchema can resolve types from adtcore
|
|
260
|
+
type AbapClass = InferSchema<typeof linkedClasses>;
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Type Inference Deep Dive
|
|
264
|
+
|
|
265
|
+
### How It Works
|
|
266
|
+
|
|
267
|
+
The type inference system uses TypeScript's conditional types to:
|
|
268
|
+
|
|
269
|
+
1. **Find root elements** - Extract element declarations from schema
|
|
270
|
+
2. **Resolve type references** - Look up `complexType` and `simpleType` by name
|
|
271
|
+
3. **Handle inheritance** - Process `complexContent/extension` for type inheritance
|
|
272
|
+
4. **Map XSD to TS** - Convert XSD types to TypeScript equivalents
|
|
273
|
+
5. **Handle optionality** - `minOccurs="0"` → optional property
|
|
274
|
+
6. **Handle arrays** - `maxOccurs="unbounded"` → array type
|
|
275
|
+
|
|
276
|
+
### Example: Complex Schema
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
const schema = {
|
|
280
|
+
$imports: [baseSchema],
|
|
281
|
+
element: [{ name: 'order', type: 'OrderType' }],
|
|
282
|
+
complexType: [
|
|
283
|
+
{
|
|
284
|
+
name: 'OrderType',
|
|
285
|
+
complexContent: {
|
|
286
|
+
extension: {
|
|
287
|
+
base: 'base:BaseEntity', // Inherits from imported schema
|
|
288
|
+
sequence: {
|
|
289
|
+
element: [
|
|
290
|
+
{ name: 'items', type: 'ItemType', maxOccurs: 'unbounded' },
|
|
291
|
+
{ name: 'total', type: 'xs:decimal' },
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: 'ItemType',
|
|
299
|
+
sequence: {
|
|
300
|
+
element: [
|
|
301
|
+
{ name: 'sku', type: 'xs:string' },
|
|
302
|
+
{ name: 'quantity', type: 'xs:int' },
|
|
303
|
+
],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
} as const;
|
|
308
|
+
|
|
309
|
+
type Order = InferSchema<typeof schema>;
|
|
310
|
+
// Result:
|
|
311
|
+
// {
|
|
312
|
+
// ...BaseEntity, // Inherited properties
|
|
313
|
+
// items: { sku: string; quantity: number }[];
|
|
314
|
+
// total: number;
|
|
315
|
+
// }
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Architecture
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
@abapify/ts-xsd
|
|
322
|
+
├── src/
|
|
323
|
+
│ ├── index.ts # Main exports
|
|
324
|
+
│ ├── xsd/ # XSD parsing, building, resolution
|
|
325
|
+
│ │ ├── types.ts # W3C 1:1 type definitions
|
|
326
|
+
│ │ ├── parse.ts # XSD XML → Schema parser
|
|
327
|
+
│ │ ├── build.ts # Schema → XSD XML builder
|
|
328
|
+
│ │ ├── resolve.ts # Schema resolver (merges imports, expands inheritance)
|
|
329
|
+
│ │ ├── traverser.ts # OO schema traversal with W3C types
|
|
330
|
+
│ │ ├── loader.ts # XSD file loading with import resolution
|
|
331
|
+
│ │ ├── schema-like.ts # Runtime schema type guards
|
|
332
|
+
│ │ └── helpers.ts # Utility functions
|
|
333
|
+
│ ├── infer/ # Compile-time type inference
|
|
334
|
+
│ │ └── types.ts # InferSchema<T>, InferElement<T>
|
|
335
|
+
│ ├── xml/ # XML parsing/building with schemas
|
|
336
|
+
│ │ ├── parse.ts # XML → Object parser
|
|
337
|
+
│ │ ├── build.ts # Object → XML builder
|
|
338
|
+
│ │ ├── typed.ts # Typed schema wrapper
|
|
339
|
+
│ │ └── dom-utils.ts # DOM manipulation utilities
|
|
340
|
+
│ ├── walker/ # Schema traversal utilities
|
|
341
|
+
│ │ └── index.ts # walkElements, walkComplexTypes, findSubstitutes
|
|
342
|
+
│ ├── codegen/ # Code generation
|
|
343
|
+
│ │ ├── generate.ts # Schema literal generator
|
|
344
|
+
│ │ ├── interface-generator.ts # Interface generator API
|
|
345
|
+
│ │ ├── ts-morph.ts # TypeScript AST manipulation
|
|
346
|
+
│ │ ├── runner.ts # Config-based codegen runner
|
|
347
|
+
│ │ └── cli.ts # CLI interface
|
|
348
|
+
│ └── generators/ # Generator plugins
|
|
349
|
+
│ ├── raw-schema.ts # Schema literal generator
|
|
350
|
+
│ ├── interfaces.ts # TypeScript interfaces generator
|
|
351
|
+
│ ├── typed-schemas.ts # Typed schema exports
|
|
352
|
+
│ └── index-barrel.ts # Index file generator
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Key Components
|
|
356
|
+
|
|
357
|
+
| Component | Purpose |
|
|
358
|
+
| ------------- | ---------------------------------------------------------------------------------- |
|
|
359
|
+
| **Resolver** | Merges `$imports`, expands `complexContent/extension`, handles `substitutionGroup` |
|
|
360
|
+
| **Traverser** | OO traversal with real W3C XSD types (SchemaTraverser class) |
|
|
361
|
+
| **Walker** | Functional iteration over schema elements, types, groups |
|
|
362
|
+
| **Loader** | File-based XSD loading with automatic import resolution |
|
|
363
|
+
|
|
364
|
+
## Design Principles
|
|
365
|
+
|
|
366
|
+
1. **Pure W3C XSD** - No invented properties or conveniences
|
|
367
|
+
2. **Type safety** - Full TypeScript support with inference
|
|
368
|
+
3. **Minimal dependencies** - Only `@xmldom/xmldom`
|
|
369
|
+
4. **Tree-shakeable** - Import only what you need
|
|
370
|
+
5. **Tested against W3C** - Verified with official XMLSchema.xsd
|
|
371
|
+
|
|
372
|
+
## Testing
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# Run all tests
|
|
376
|
+
npx nx test ts-xsd
|
|
377
|
+
|
|
378
|
+
# Run with coverage
|
|
379
|
+
npx nx test:coverage ts-xsd
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Tests include:
|
|
383
|
+
|
|
384
|
+
- Unit tests for parser, builder, and inference
|
|
385
|
+
- Integration tests with real XSD files
|
|
386
|
+
- W3C XMLSchema.xsd roundtrip verification
|
|
387
|
+
|
|
388
|
+
## Related Packages
|
|
389
|
+
|
|
390
|
+
- **[@abapify/adt-schemas](../adt-schemas)** - SAP ADT schemas using ts-xsd
|
|
391
|
+
- **[@abapify/adt-contracts](../adt-contracts)** - REST contracts for SAP ADT APIs
|
|
392
|
+
- **[@abapify/adt-plugin-abapgit](../adt-plugin-abapgit)** - abapGit plugin schemas
|
|
393
|
+
|
|
394
|
+
## Documentation
|
|
395
|
+
|
|
396
|
+
- **[Codegen Guide](./docs/codegen.md)** - Comprehensive code generation documentation
|
|
397
|
+
- **[AGENTS.md](./AGENTS.md)** - AI agent guidelines
|
|
398
|
+
|
|
399
|
+
## References
|
|
400
|
+
|
|
401
|
+
- [W3C XML Schema 1.1 Part 1: Structures](https://www.w3.org/TR/xmlschema11-1/)
|
|
402
|
+
- [XMLSchema.xsd](https://www.w3.org/TR/xmlschema11-1/XMLSchema.xsd)
|
|
403
|
+
|
|
404
|
+
## License
|
|
405
|
+
|
|
406
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abapify/ts-xsd",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
|
|
3
|
+
"version": "0.3.4",
|
|
4
|
+
"description": "Core XSD parser and builder - W3C XMLSchema 1:1 TypeScript representation",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"types": "./dist/index.d.mts",
|
|
11
|
+
"bin": {
|
|
12
|
+
"ts-xsd": "./dist/codegen/cli.mjs"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": "./dist/index.mjs",
|
|
16
|
+
"./codegen/cli": "./dist/codegen/cli.mjs",
|
|
17
|
+
"./generators": "./dist/generators/index.mjs",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@xmldom/xmldom": "^0.9.9",
|
|
26
|
+
"ts-morph": "*",
|
|
27
|
+
"zod": "^3.24.0 || ^4.0.0"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/abapify/adt-cli.git",
|
|
32
|
+
"directory": "packages/ts-xsd"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/abapify/adt-cli/tree/main/packages/ts-xsd#readme",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/abapify/adt-cli/issues"
|
|
37
|
+
}
|
|
38
|
+
}
|