@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 +17 -320
- package/dist/codegen/cli.mjs +6 -4
- package/dist/codegen/cli.mjs.map +1 -1
- package/dist/{generate-DQJ38rjv.mjs → generate-BWLnwHUW.mjs} +38 -33
- package/dist/generate-BWLnwHUW.mjs.map +1 -0
- package/dist/generators/index.d.mts +1 -1
- package/dist/generators/index.mjs +130 -116
- package/dist/generators/index.mjs.map +1 -1
- package/dist/index.d.mts +6 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +495 -368
- package/dist/index.mjs.map +1 -1
- package/dist/{runner-BGNA8Sl-.mjs → runner-BZNOc_YZ.mjs} +439 -334
- package/dist/runner-BZNOc_YZ.mjs.map +1 -0
- package/dist/{runner-CDEKB5AE.d.mts → runner-x86T9IvM.d.mts} +5 -5
- package/dist/{runner-CDEKB5AE.d.mts.map → runner-x86T9IvM.d.mts.map} +1 -1
- package/dist/{types-CySWjUcM.mjs → types-BAx8yQfm.mjs} +151 -115
- package/dist/types-BAx8yQfm.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/generate-DQJ38rjv.mjs.map +0 -1
- package/dist/runner-BGNA8Sl-.mjs.map +0 -1
- package/dist/types-CySWjUcM.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,338 +1,35 @@
|
|
|
1
|
-
# ts-xsd
|
|
1
|
+
# @abapify/ts-xsd
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@abapify/ts-xsd)
|
|
4
|
+
[](https://github.com/abapify/adt-cli/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
W3C XSD 1.1 parser, builder, and TypeScript type inference.
|
|
7
7
|
|
|
8
|
-
##
|
|
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
|
-
|
|
14
|
+
## Usage
|
|
29
15
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
### Parse and Build XSD
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
16
|
+
```ts
|
|
35
17
|
import { parseXsd, buildXsd } from '@abapify/ts-xsd';
|
|
36
18
|
|
|
37
|
-
|
|
38
|
-
const
|
|
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
|
-
##
|
|
23
|
+
## Features
|
|
323
24
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
##
|
|
29
|
+
## Documentation
|
|
331
30
|
|
|
332
|
-
-
|
|
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
|
-
|
|
33
|
+
---
|
|
337
34
|
|
|
338
|
-
[
|
|
35
|
+
Part of [**abapify**](https://github.com/abapify/adt-cli) — open, typed tooling for SAP ABAP Development Tools (ADT): CLI, MCP server, and SDK.
|
package/dist/codegen/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as runCodegen } from "../runner-
|
|
3
|
-
import { t as generateSchemaFile } from "../generate-
|
|
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
|
-
|
|
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
|
package/dist/codegen/cli.mjs.map
CHANGED
|
@@ -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\
|
|
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-
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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 "
|
|
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-
|
|
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-
|
|
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;
|