# ts-xsd Codegen Module

**Generate TypeScript from XSD** - Transform XSD schemas into type-safe TypeScript code.

## Overview

The codegen module provides tools to generate TypeScript code from XSD schema definitions:

| Generator               | Output                    | Use Case                             |
| ----------------------- | ------------------------- | ------------------------------------ |
| `generateSchemaLiteral` | Schema `as const` literal | Type inference with `InferSchema<T>` |
| `generateSchemaFile`    | Complete TypeScript file  | Standalone schema modules            |
| `generateInterfaces`    | TypeScript interfaces     | Pre-computed types (avoids TS2589)   |

## Why Code Generation?

### Problem: TypeScript Recursion Limits

TypeScript's type inference has depth limits. Complex schemas with many nested types can trigger:

```
TS2589: Type instantiation is excessively deep and possibly infinite.
```

### Solution: Build-Time Generation

Instead of relying on compile-time inference:

```typescript
// ❌ May hit recursion limits with complex schemas
type Data = InferSchema<typeof complexSchema>;
```

Generate interfaces at build time:

```typescript
// ✅ Pre-computed, no recursion limits
import type { AbapClass } from './generated/types';
```

## Schema Literal Generator

### `generateSchemaLiteral(xsd, options)`

Transforms XSD content into a TypeScript schema literal with `as const`.

```typescript
import { generateSchemaLiteral } from '@abapify/ts-xsd';

const xsd = `
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="person" type="PersonType"/>
    <xs:complexType name="PersonType">
      <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="age" type="xs:int" minOccurs="0"/>
      </xs:sequence>
    </xs:complexType>
  </xs:schema>
`;

const code = generateSchemaLiteral(xsd, {
  name: 'person',
  features: { $xmlns: true, $imports: true, $filename: true },
  exclude: ['annotation'],
});
```

**Output:**

```typescript
export const person = {
  $filename: 'person.xsd',
  $xmlns: {
    xs: 'http://www.w3.org/2001/XMLSchema',
  },
  element: [{ name: 'person', type: 'PersonType' }],
  complexType: [
    {
      name: 'PersonType',
      sequence: {
        element: [
          { name: 'name', type: 'xs:string' },
          { name: 'age', type: 'xs:int', minOccurs: '0' },
        ],
      },
    },
  ],
} as const;
```

### Options

| Option           | Type       | Description                                     |
| ---------------- | ---------- | ----------------------------------------------- |
| `name`           | `string`   | Variable name for exported schema               |
| `comment`        | `string`   | JSDoc comment                                   |
| `pretty`         | `boolean`  | Pretty print with indentation (default: `true`) |
| `indent`         | `string`   | Indentation string (default: `'  '`)            |
| `features`       | `object`   | Enable `$` extension properties                 |
| `exclude`        | `string[]` | Property names to exclude                       |
| `importResolver` | `function` | Resolve import paths                            |

### Features

| Feature     | Description                                          |
| ----------- | ---------------------------------------------------- |
| `$xmlns`    | Include namespace declarations                       |
| `$imports`  | Convert `import` to linked schema references         |
| `$filename` | Include source filename (enables backward rendering) |

## Schema File Generator

### `generateSchemaFile(xsd, options)`

Generates a complete TypeScript file with imports and type exports.

```typescript
import { generateSchemaFile } from '@abapify/ts-xsd';

const code = generateSchemaFile(xsd, {
  name: 'classes',
  features: { $xmlns: true, $imports: true, $filename: true },
  importResolver: (location) => {
    // adtcore.xsd → ./adtcore
    return './' + location.replace('.xsd', '');
  },
});
```

**Output:**

```typescript
/**
 * Auto-generated schema literal from XSD
 *
 * DO NOT EDIT - Generated by ts-xsd codegen
 */

import { adtcore } from './adtcore';
import { abapoo } from './abapoo';

export const classes = {
  $filename: "classes.xsd",
  $xmlns: { ... },
  $imports: [adtcore, abapoo],
  targetNamespace: "http://www.sap.com/adt/oo/classes",
  // ...
} as const;

export type ClassesType = typeof classes;
```

## Interface Generator

### `generateInterfaces(schema, options)`

Generates TypeScript interfaces from a parsed schema. Uses `ts-morph` for AST manipulation. Returns an object with `code` property.

```typescript
import { parseXsd, generateInterfaces } from '@abapify/ts-xsd';

const schema = parseXsd(xsdContent);
const { code } = generateInterfaces(schema, {
  flatten: true, // Inline all nested types
  addJsDoc: true, // Add JSDoc comments
  rootTypeName: 'PersonSchema', // Custom root type name
});
```

**Output (flatten: false - default):**

```typescript
/** Generated from complexType: PersonType */
export interface PersonType {
  name: string;
  age?: number;
}

/** Generated from complexType: AddressType */
export interface AddressType {
  street: string;
  city: string;
  country?: string;
}

export type PersonSchema = {
  person: PersonType;
};
```

**Output (flatten: true):**

```typescript
export type PersonSchema = {
  person: {
    name: string;
    age?: number;
  };
};
```

### Options

| Option         | Type      | Description                                                 |
| -------------- | --------- | ----------------------------------------------------------- |
| `flatten`      | `boolean` | Inline all nested types into single type (default: `false`) |
| `addJsDoc`     | `boolean` | Add JSDoc comments                                          |
| `rootTypeName` | `string`  | Custom name for root type                                   |
| `comment`      | `string`  | Header comment for generated file                           |

### Flatten Mode

The `flatten: true` option inlines all type references, producing a single self-contained type. This is useful for:

- **Simpler types** - No separate interface definitions
- **Better IDE tooltips** - Full type visible on hover
- **Avoiding import issues** - No cross-file type dependencies

**Cycle Detection:** The flattener includes cycle detection to prevent stack overflow on circular type references. When a cycle is detected, it outputs `unknown` for the recursive reference.

### Cross-Schema Type Resolution

The interface generator resolves types across linked schemas:

```typescript
// Schema with $imports
const schema = {
  $imports: [adtcore, abapoo],
  complexType: [
    {
      name: 'AbapClass',
      complexContent: {
        extension: { base: 'abapoo:AbapOoObject' }, // Resolved!
      },
    },
  ],
};

const interfaces = generateInterfaces(schema, { generateAllTypes: true });
// Generates: interface AbapClass extends AbapOoObject { ... }
```

## Config-Based Generator System

The `generators/` module provides a config-based approach for batch schema generation.

### Configuration File (`ts-xsd.config.ts`)

```typescript
import {
  defineConfig,
  rawSchema,
  interfaces,
} from '@abapify/ts-xsd/generators';

export default defineConfig({
  schemas: [
    { name: 'adtcore', xsd: '.xsd/sap/adtcore.xsd' },
    { name: 'classes', xsd: '.xsd/sap/classes.xsd', imports: ['adtcore'] },
  ],
  generators: [
    rawSchema({
      outputDir: 'src/schemas/generated/schemas',
      features: { $xmlns: true, $imports: true, $filename: true },
    }),
    interfaces({
      outputDir: 'src/schemas/generated/types',
      flatten: true, // Inline all types
    }),
  ],
});
```

### Built-in Generators

| Generator    | Output            | Description                    |
| ------------ | ----------------- | ------------------------------ |
| `rawSchema`  | `schema.ts`       | Schema literal with `as const` |
| `interfaces` | `schema.types.ts` | TypeScript interfaces/types    |

### Generator Lifecycle

```typescript
interface Generator {
  name: string;
  generate(schema: Schema, ctx: GeneratorContext): Promise<GeneratorResult>;
  finalize?(ctx: FinalizeContext): Promise<void>; // Optional afterAll hook
}
```

### Running Generators

```bash
# Via Nx target
npx nx codegen my-package

# Programmatically
import { runGenerators } from '@abapify/ts-xsd/generators';
await runGenerators(config);
```

## Usage in adt-schemas

The codegen module powers the schema generation pipeline:

```
XSD Files (.xsd/model/)
    ↓ generateSchemaLiteral
Schema Literals (as const)
    ↓ generateInterfaces
TypeScript Interfaces (204 types)
    ↓ typed() wrapper
Typed Schemas (parse/build)
```

### Generation Script Example

```typescript
import {
  generateSchemaLiteral,
  generateInterfaces,
  parseXsd,
} from '@abapify/ts-xsd';
import { readFileSync, writeFileSync } from 'fs';

// 1. Generate schema literal
const xsd = readFileSync('classes.xsd', 'utf-8');
const schemaCode = generateSchemaLiteral(xsd, {
  name: 'classes',
  features: { $xmlns: true, $imports: true, $filename: true },
  exclude: ['annotation'],
  importResolver: (loc) => `./${loc.replace('.xsd', '')}`,
});
writeFileSync('schemas/classes.ts', schemaCode);

// 2. Generate interfaces
const schema = parseXsd(xsd);
const interfaces = generateInterfaces(schema, { generateAllTypes: true });
writeFileSync('types/classes.ts', interfaces);
```

## Best Practices

### 1. Always Enable `$filename`

Required for backward rendering (rebuilding XSD from schema):

```typescript
features: {
  $filename: true;
}
```

### 2. Exclude Annotations

Annotations add bulk without affecting type inference:

```typescript
exclude: ['annotation'];
```

### 3. Use Import Resolver for Multi-Schema Projects

```typescript
importResolver: (schemaLocation) => {
  // Map XSD imports to TypeScript imports
  const name = schemaLocation.replace('.xsd', '');
  return `./schemas/${name}`;
};
```

### 4. Generate Interfaces for Complex Schemas

Avoid TypeScript recursion limits:

```typescript
// For schemas with 50+ types, generate interfaces
const interfaces = generateInterfaces(schema, { generateAllTypes: true });
```

### 5. Verify with Round-Trip Tests

Ensure generated schemas work correctly:

```typescript
import { parseXml, buildXml } from '@abapify/ts-xsd';
import { classes } from './generated/classes';

const data = parseXml(classes, xmlString);
const rebuilt = buildXml(classes, data);
// Verify rebuilt matches original structure
```

## CLI Usage

The codegen module includes a CLI for batch generation:

```bash
# Generate schema literal
npx ts-xsd codegen -i schema.xsd -o schema.ts

# With options
npx ts-xsd codegen -i schema.xsd -o schema.ts \
  --name mySchema \
  --features '$xmlns,$imports,$filename' \
  --exclude annotation
```

## Reference

- [ts-xsd README](../README.md)
- [ts-xsd AGENTS.md](../AGENTS.md)
- [adt-schemas](../../adt-schemas/README.md) - Real-world usage
