klasik 1.0.19 → 1.0.22
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/.claude/settings.local.json +18 -0
- package/README.md +247 -0
- package/dist/cli.js +6 -0
- package/dist/esm-fixer.d.ts +29 -0
- package/dist/esm-fixer.js +139 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/k8s-client-generator.d.ts +30 -0
- package/dist/k8s-client-generator.js +52 -1
- package/package.json +7 -3
- package/{test-discriminated-output/configuration.ts → templates/configuration.mustache} +4 -12
- package/test-discriminated-output/.openapi-generator-ignore +0 -23
- package/test-discriminated-output/api/default-api.ts +0 -142
- package/test-discriminated-output/api.ts +0 -19
- package/test-discriminated-output/base.ts +0 -86
- package/test-discriminated-output/common.ts +0 -151
- package/test-discriminated-output/index.ts +0 -18
- package/test-discriminated-output/models/capability.js +0 -80
- package/test-discriminated-output/models/capability.ts +0 -74
- package/test-discriminated-output/models/helm-component.js +0 -82
- package/test-discriminated-output/models/helm-component.ts +0 -83
- package/test-discriminated-output/models/index.ts +0 -4
- package/test-discriminated-output/models/kustomize-component.js +0 -70
- package/test-discriminated-output/models/kustomize-component.ts +0 -70
- package/test-discriminated-output/models/manifest-component.js +0 -58
- package/test-discriminated-output/models/manifest-component.ts +0 -57
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(grep:*)",
|
|
5
|
+
"WebSearch",
|
|
6
|
+
"Bash(npx @openapitools/openapi-generator-cli help generate:*)",
|
|
7
|
+
"Bash(node dist/cli.js generate:*)",
|
|
8
|
+
"Bash(npx tsc:*)",
|
|
9
|
+
"Bash(npm install:*)",
|
|
10
|
+
"WebFetch(domain:dev.to)",
|
|
11
|
+
"WebFetch(domain:saintlouvent.com)",
|
|
12
|
+
"WebFetch(domain:zod.dev)",
|
|
13
|
+
"Bash(node test-disc-union.js:*)",
|
|
14
|
+
"Bash(node test-disc-code.js:*)",
|
|
15
|
+
"Bash(node test-new-validators.js:*)"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ Download OpenAPI specifications from remote URLs and generate TypeScript clients
|
|
|
9
9
|
- 📄 **JSON and YAML support** - Automatically parse and handle both formats (YAML specs are converted to JSON for code generation)
|
|
10
10
|
- 🔐 **Authentication support** - Custom headers including Bearer tokens and API keys
|
|
11
11
|
- 🔗 **External reference resolution** - Automatically download referenced schema files (`$ref`)
|
|
12
|
+
- 📦 **ESM compatibility** - Automatic `.js` extension fixing for Node.js ESM projects
|
|
12
13
|
- 🔄 **Automatic transformation** - Converts API responses to class instances using class-transformer
|
|
13
14
|
- 🎯 **Type-safe** - Full TypeScript support with decorators
|
|
14
15
|
- 🛠️ **Configurable** - Custom headers, timeouts, and templates
|
|
@@ -102,6 +103,15 @@ Generate a TypeScript client from an OpenAPI spec (remote URL or local file).
|
|
|
102
103
|
- Can be used multiple times for multiple headers
|
|
103
104
|
- Perfect for authorization: `--header "Authorization: Bearer token"`
|
|
104
105
|
- `-r, --resolve-refs` - Resolve and download external `$ref` references
|
|
106
|
+
- `--esm` - Add `.js` extensions to imports for ESM compatibility (Node.js modules)
|
|
107
|
+
- `--nestjs-swagger` - Include NestJS Swagger `@ApiProperty` decorators in generated models
|
|
108
|
+
- Automatically adds `@nestjs/swagger` to generated package.json
|
|
109
|
+
- Ideal for NestJS applications with Swagger/OpenAPI documentation
|
|
110
|
+
- Works with both `full` and `models-only` generation modes
|
|
111
|
+
- `--class-validator` - Include class-validator decorators for runtime validation (requires openapi-class-transformer v2.0+)
|
|
112
|
+
- Automatically adds `class-validator` and `class-transformer` to generated package.json
|
|
113
|
+
- Enables runtime validation with decorators like `@IsString`, `@IsEmail`, `@Min`, `@Max`
|
|
114
|
+
- Forward-compatible flag - full decorator generation coming in future version
|
|
105
115
|
- `-t, --template <dir>` - Custom template directory (klasik includes enhanced TSDoc templates by default)
|
|
106
116
|
- `-k, --keep-spec` - Keep the downloaded spec file after generation
|
|
107
117
|
- `--timeout <ms>` - Request timeout in milliseconds for HTTP requests (default: 30000)
|
|
@@ -123,6 +133,28 @@ klasik generate \
|
|
|
123
133
|
--header "Authorization: Bearer token123" \
|
|
124
134
|
--resolve-refs
|
|
125
135
|
|
|
136
|
+
# NestJS integration with Swagger decorators
|
|
137
|
+
klasik generate \
|
|
138
|
+
--url https://api.example.com/openapi.json \
|
|
139
|
+
--output ./src/generated \
|
|
140
|
+
--mode models-only \
|
|
141
|
+
--nestjs-swagger
|
|
142
|
+
|
|
143
|
+
# With runtime validation decorators
|
|
144
|
+
klasik generate \
|
|
145
|
+
--url https://api.example.com/openapi.json \
|
|
146
|
+
--output ./src/generated \
|
|
147
|
+
--mode models-only \
|
|
148
|
+
--class-validator
|
|
149
|
+
|
|
150
|
+
# Full NestJS setup: Swagger docs + validation
|
|
151
|
+
klasik generate \
|
|
152
|
+
--url https://api.example.com/openapi.json \
|
|
153
|
+
--output ./src/generated \
|
|
154
|
+
--mode models-only \
|
|
155
|
+
--nestjs-swagger \
|
|
156
|
+
--class-validator
|
|
157
|
+
|
|
126
158
|
# Multiple headers
|
|
127
159
|
klasik generate \
|
|
128
160
|
--url https://api.example.com/openapi.json \
|
|
@@ -181,11 +213,14 @@ const generator = new K8sClientGenerator();
|
|
|
181
213
|
await generator.generate({
|
|
182
214
|
specUrl: 'https://api.example.com/openapi.json',
|
|
183
215
|
outputDir: './client',
|
|
216
|
+
mode: 'models-only',
|
|
184
217
|
headers: {
|
|
185
218
|
'Authorization': 'Bearer token123',
|
|
186
219
|
'X-Custom-Header': 'value'
|
|
187
220
|
},
|
|
188
221
|
resolveReferences: true, // Download external $ref files
|
|
222
|
+
nestJsSwagger: true, // Include @ApiProperty decorators
|
|
223
|
+
classValidator: true, // Include validation decorators
|
|
189
224
|
templateDir: './custom-templates', // Optional
|
|
190
225
|
keepSpec: true, // Keep downloaded spec file
|
|
191
226
|
timeout: 60000 // Request timeout in ms
|
|
@@ -227,6 +262,8 @@ The generated TypeScript client includes:
|
|
|
227
262
|
- ✅ **Vendor extensions** - Preserved in JSDoc comments and metadata
|
|
228
263
|
- ✅ **Error handling** - Configurable transformation error handlers
|
|
229
264
|
- ✅ **Configuration options** - Enable/disable transformation, custom error handlers
|
|
265
|
+
- ✅ **NestJS Swagger decorators** (optional) - `@ApiProperty` with type, description, example, required, and nullable when `--nestjs-swagger` is enabled
|
|
266
|
+
- ✅ **class-validator decorators** (optional) - Runtime validation decorators when `--class-validator` is enabled (full support in v2.0+)
|
|
230
267
|
|
|
231
268
|
### Enhanced TSDoc Documentation
|
|
232
269
|
|
|
@@ -420,6 +457,167 @@ await new K8sClientGenerator().generate({
|
|
|
420
457
|
});
|
|
421
458
|
```
|
|
422
459
|
|
|
460
|
+
## NestJS Integration
|
|
461
|
+
|
|
462
|
+
### Using with NestJS Applications
|
|
463
|
+
|
|
464
|
+
Klasik-generated models integrate seamlessly with NestJS applications. Use `--mode models-only` with decorator flags for best results.
|
|
465
|
+
|
|
466
|
+
#### Swagger Documentation Support
|
|
467
|
+
|
|
468
|
+
Generate models with `@ApiProperty` decorators for automatic Swagger documentation:
|
|
469
|
+
|
|
470
|
+
```bash
|
|
471
|
+
npx klasik generate \
|
|
472
|
+
--url https://api.example.com/openapi.json \
|
|
473
|
+
--output ./src/generated/dto \
|
|
474
|
+
--mode models-only \
|
|
475
|
+
--nestjs-swagger
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
This generates DTOs like:
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
import { Expose } from 'class-transformer';
|
|
482
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
483
|
+
|
|
484
|
+
export class UserDto {
|
|
485
|
+
@ApiProperty({
|
|
486
|
+
type: String,
|
|
487
|
+
description: 'Unique identifier',
|
|
488
|
+
required: true,
|
|
489
|
+
example: '123e4567-e89b-41d3-a456-426614174000',
|
|
490
|
+
})
|
|
491
|
+
@Expose()
|
|
492
|
+
id: string;
|
|
493
|
+
|
|
494
|
+
@ApiProperty({
|
|
495
|
+
type: String,
|
|
496
|
+
description: 'User email address',
|
|
497
|
+
required: true,
|
|
498
|
+
format: 'email',
|
|
499
|
+
})
|
|
500
|
+
@Expose()
|
|
501
|
+
email: string;
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Use in your NestJS controllers:
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
import { Controller, Get, Post, Body } from '@nestjs/common';
|
|
509
|
+
import { ApiTags, ApiResponse } from '@nestjs/swagger';
|
|
510
|
+
import { UserDto } from './generated/dto';
|
|
511
|
+
|
|
512
|
+
@ApiTags('users')
|
|
513
|
+
@Controller('users')
|
|
514
|
+
export class UsersController {
|
|
515
|
+
@Get()
|
|
516
|
+
@ApiResponse({ type: [UserDto], status: 200 })
|
|
517
|
+
async findAll(): Promise<UserDto[]> {
|
|
518
|
+
// Your implementation
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
@Post()
|
|
522
|
+
@ApiResponse({ type: UserDto, status: 201 })
|
|
523
|
+
async create(@Body() createUserDto: UserDto): Promise<UserDto> {
|
|
524
|
+
// Your implementation
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Your Swagger UI automatically displays complete documentation from the `@ApiProperty` decorators.
|
|
530
|
+
|
|
531
|
+
#### Runtime Validation Support
|
|
532
|
+
|
|
533
|
+
Add the `--class-validator` flag for runtime validation decorators:
|
|
534
|
+
|
|
535
|
+
```bash
|
|
536
|
+
npx klasik generate \
|
|
537
|
+
--url https://api.example.com/openapi.json \
|
|
538
|
+
--output ./src/generated/dto \
|
|
539
|
+
--mode models-only \
|
|
540
|
+
--nestjs-swagger \
|
|
541
|
+
--class-validator
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Note**: Full decorator generation requires openapi-class-transformer v2.0+. For now, the dependencies are added to package.json, and you can manually add validation decorators:
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
import { Expose } from 'class-transformer';
|
|
548
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
549
|
+
import { IsString, IsEmail } from 'class-validator';
|
|
550
|
+
|
|
551
|
+
export class UserDto {
|
|
552
|
+
@ApiProperty({ type: String, required: true })
|
|
553
|
+
@IsString()
|
|
554
|
+
@Expose()
|
|
555
|
+
id: string;
|
|
556
|
+
|
|
557
|
+
@ApiProperty({ type: String, required: true, format: 'email' })
|
|
558
|
+
@IsEmail()
|
|
559
|
+
@Expose()
|
|
560
|
+
email: string;
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
Use with NestJS ValidationPipe:
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
568
|
+
|
|
569
|
+
// In main.ts
|
|
570
|
+
app.useGlobalPipes(new ValidationPipe({
|
|
571
|
+
transform: true,
|
|
572
|
+
whitelist: true,
|
|
573
|
+
}));
|
|
574
|
+
|
|
575
|
+
// In controller
|
|
576
|
+
@Post()
|
|
577
|
+
async create(@Body() dto: UserDto): Promise<UserDto> {
|
|
578
|
+
// dto is already validated and transformed to UserDto instance!
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
#### Required Dependencies for NestJS
|
|
583
|
+
|
|
584
|
+
When using `--nestjs-swagger` or `--class-validator`, ensure your project has these peer dependencies:
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
npm install @nestjs/swagger swagger-ui-express
|
|
588
|
+
npm install class-validator class-transformer
|
|
589
|
+
npm install reflect-metadata
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Update your `tsconfig.json`:
|
|
593
|
+
|
|
594
|
+
```json
|
|
595
|
+
{
|
|
596
|
+
"compilerOptions": {
|
|
597
|
+
"experimentalDecorators": true,
|
|
598
|
+
"emitDecoratorMetadata": true
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
#### Best Practices
|
|
604
|
+
|
|
605
|
+
1. **Use models-only mode**: Generate only DTOs, keep your NestJS business logic separate
|
|
606
|
+
2. **Separate directories**: Generate into `src/generated/dto` or similar
|
|
607
|
+
3. **Extend generated classes**: For complex validation, extend generated DTOs
|
|
608
|
+
4. **Version control**: Commit generated files or regenerate in CI/CD
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
// Custom DTO extending generated model
|
|
612
|
+
import { UserDto } from '../generated/dto';
|
|
613
|
+
import { IsStrongPassword } from 'class-validator';
|
|
614
|
+
|
|
615
|
+
export class CreateUserDto extends UserDto {
|
|
616
|
+
@IsStrongPassword()
|
|
617
|
+
password: string;
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
423
621
|
## Best Practices for Existing Projects
|
|
424
622
|
|
|
425
623
|
When integrating klasik into an existing TypeScript project, follow these best practices to avoid conflicts:
|
|
@@ -492,6 +690,55 @@ npx klasik generate \
|
|
|
492
690
|
--mode full # Generates complete client with APIs
|
|
493
691
|
```
|
|
494
692
|
|
|
693
|
+
### ESM (ECMAScript Modules) Support
|
|
694
|
+
|
|
695
|
+
If your project uses ECMAScript Modules (`"type": "module"` in package.json), use the `--esm` flag to automatically add `.js` extensions to all relative imports:
|
|
696
|
+
|
|
697
|
+
```bash
|
|
698
|
+
# Generate ESM-compatible code
|
|
699
|
+
npx klasik generate \
|
|
700
|
+
--url https://api.example.com/openapi.yaml \
|
|
701
|
+
--output ./src/generated \
|
|
702
|
+
--mode models-only \
|
|
703
|
+
--esm # ✅ Adds .js extensions to imports
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
**What `--esm` does:**
|
|
707
|
+
- Adds `.js` extensions to all relative imports: `from './user'` → `from './user.js'`
|
|
708
|
+
- Adds `.js` extensions to all relative exports: `export * from './api'` → `export * from './api.js'`
|
|
709
|
+
- Handles directory imports: `from './models'` → `from './models/index.js'`
|
|
710
|
+
- Only affects relative imports (doesn't touch external packages like `axios`, `class-transformer`)
|
|
711
|
+
|
|
712
|
+
**Before `--esm`:**
|
|
713
|
+
```typescript
|
|
714
|
+
import { User } from './models/user';
|
|
715
|
+
import { Cluster } from '../models/cluster';
|
|
716
|
+
export * from './api/orgs-api';
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
**After `--esm`:**
|
|
720
|
+
```typescript
|
|
721
|
+
import { User } from './models/user.js';
|
|
722
|
+
import { Cluster } from '../models/cluster.js';
|
|
723
|
+
export * from './api/orgs-api.js';
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
**When to use `--esm`:**
|
|
727
|
+
- Your package.json has `"type": "module"`
|
|
728
|
+
- You're using Node.js native ESM
|
|
729
|
+
- You're targeting modern JavaScript environments
|
|
730
|
+
- You want to eliminate post-processing scripts
|
|
731
|
+
|
|
732
|
+
**package.json setup for ESM:**
|
|
733
|
+
```json
|
|
734
|
+
{
|
|
735
|
+
"type": "module",
|
|
736
|
+
"scripts": {
|
|
737
|
+
"generate-client": "npx klasik generate --url $API_URL --output src/generated --mode models-only --esm"
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
495
742
|
## Advanced Configuration
|
|
496
743
|
|
|
497
744
|
### Custom Error Handling
|
package/dist/cli.js
CHANGED
|
@@ -52,6 +52,9 @@ program
|
|
|
52
52
|
.option('-t, --template <dir>', 'Custom template directory')
|
|
53
53
|
.option('-k, --keep-spec', 'Keep the downloaded spec file after generation', false)
|
|
54
54
|
.option('-r, --resolve-refs', 'Resolve and download external $ref references', false)
|
|
55
|
+
.option('--esm', 'Add .js extensions to imports for ESM compatibility', false)
|
|
56
|
+
.option('--nestjs-swagger', 'Include NestJS Swagger @ApiProperty decorators in generated models', false)
|
|
57
|
+
.option('--class-validator', 'Include class-validator decorators in generated models', false)
|
|
55
58
|
.option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
|
|
56
59
|
.action(async (options) => {
|
|
57
60
|
try {
|
|
@@ -82,6 +85,9 @@ program
|
|
|
82
85
|
templateDir: options.template,
|
|
83
86
|
keepSpec: options.keepSpec,
|
|
84
87
|
resolveReferences: options.resolveRefs,
|
|
88
|
+
fixEsmImports: options.esm,
|
|
89
|
+
nestJsSwagger: options.nestjsSwagger,
|
|
90
|
+
classValidator: options.classValidator,
|
|
85
91
|
timeout: parseInt(options.timeout, 10),
|
|
86
92
|
});
|
|
87
93
|
process.exit(0);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixes relative imports in TypeScript files to include .js extensions for ESM compatibility
|
|
3
|
+
*
|
|
4
|
+
* Node.js ESM requires explicit file extensions for relative imports.
|
|
5
|
+
* This utility adds .js extensions to all relative import/export statements.
|
|
6
|
+
*/
|
|
7
|
+
export declare class EsmFixer {
|
|
8
|
+
/**
|
|
9
|
+
* Fix all TypeScript files in a directory recursively
|
|
10
|
+
*/
|
|
11
|
+
fixDirectory(directory: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Fix imports in a single file
|
|
14
|
+
* @returns true if file was modified, false otherwise
|
|
15
|
+
*/
|
|
16
|
+
private fixFile;
|
|
17
|
+
/**
|
|
18
|
+
* Fix import statements to add .js extensions
|
|
19
|
+
*/
|
|
20
|
+
private fixImports;
|
|
21
|
+
/**
|
|
22
|
+
* Fix export statements to add .js extensions
|
|
23
|
+
*/
|
|
24
|
+
private fixExports;
|
|
25
|
+
/**
|
|
26
|
+
* Get all TypeScript files in a directory recursively
|
|
27
|
+
*/
|
|
28
|
+
private getAllTsFiles;
|
|
29
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.EsmFixer = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Fixes relative imports in TypeScript files to include .js extensions for ESM compatibility
|
|
41
|
+
*
|
|
42
|
+
* Node.js ESM requires explicit file extensions for relative imports.
|
|
43
|
+
* This utility adds .js extensions to all relative import/export statements.
|
|
44
|
+
*/
|
|
45
|
+
class EsmFixer {
|
|
46
|
+
/**
|
|
47
|
+
* Fix all TypeScript files in a directory recursively
|
|
48
|
+
*/
|
|
49
|
+
async fixDirectory(directory) {
|
|
50
|
+
console.log('Fixing ESM imports to add .js extensions...');
|
|
51
|
+
const files = this.getAllTsFiles(directory);
|
|
52
|
+
let fixedCount = 0;
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
const fixed = await this.fixFile(file);
|
|
55
|
+
if (fixed) {
|
|
56
|
+
fixedCount++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
console.log(`✅ Fixed ${fixedCount} file(s) for ESM compatibility`);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Fix imports in a single file
|
|
63
|
+
* @returns true if file was modified, false otherwise
|
|
64
|
+
*/
|
|
65
|
+
async fixFile(filePath) {
|
|
66
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
67
|
+
const originalContent = content;
|
|
68
|
+
// Fix import statements
|
|
69
|
+
content = this.fixImports(content);
|
|
70
|
+
// Fix export statements
|
|
71
|
+
content = this.fixExports(content);
|
|
72
|
+
// Only write if content changed
|
|
73
|
+
if (content !== originalContent) {
|
|
74
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Fix import statements to add .js extensions
|
|
81
|
+
*/
|
|
82
|
+
fixImports(content) {
|
|
83
|
+
// Match: import ... from './relative/path'
|
|
84
|
+
// Don't match if already has extension (.js, .ts, .json)
|
|
85
|
+
// Don't match external packages (no . or ..)
|
|
86
|
+
const importRegex = /from\s+(['"])(\.|\.\.)(\/[^'"]*?)(?<!\.js|\.ts|\.json|\.mjs|\.cjs)\1/g;
|
|
87
|
+
return content.replace(importRegex, (match, quote, dots, importPath) => {
|
|
88
|
+
// Handle index imports: './models' -> './models/index.js'
|
|
89
|
+
// But only if the path doesn't already end with a filename
|
|
90
|
+
const hasFilename = /\/[^/]+$/.test(importPath);
|
|
91
|
+
if (!hasFilename && importPath.length > 0) {
|
|
92
|
+
// It's a directory import, add /index.js
|
|
93
|
+
return `from ${quote}${dots}${importPath}/index.js${quote}`;
|
|
94
|
+
}
|
|
95
|
+
// Normal file import, just add .js
|
|
96
|
+
return `from ${quote}${dots}${importPath}.js${quote}`;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Fix export statements to add .js extensions
|
|
101
|
+
*/
|
|
102
|
+
fixExports(content) {
|
|
103
|
+
// Match: export ... from './relative/path'
|
|
104
|
+
// Don't match if already has extension
|
|
105
|
+
const exportRegex = /from\s+(['"])(\.|\.\.)(\/[^'"]*?)(?<!\.js|\.ts|\.json|\.mjs|\.cjs)\1/g;
|
|
106
|
+
return content.replace(exportRegex, (match, quote, dots, exportPath) => {
|
|
107
|
+
// Handle index exports
|
|
108
|
+
const hasFilename = /\/[^/]+$/.test(exportPath);
|
|
109
|
+
if (!hasFilename && exportPath.length > 0) {
|
|
110
|
+
return `from ${quote}${dots}${exportPath}/index.js${quote}`;
|
|
111
|
+
}
|
|
112
|
+
return `from ${quote}${dots}${exportPath}.js${quote}`;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get all TypeScript files in a directory recursively
|
|
117
|
+
*/
|
|
118
|
+
getAllTsFiles(directory) {
|
|
119
|
+
const files = [];
|
|
120
|
+
const walk = (dir) => {
|
|
121
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
122
|
+
for (const entry of entries) {
|
|
123
|
+
const fullPath = path.join(dir, entry.name);
|
|
124
|
+
if (entry.isDirectory()) {
|
|
125
|
+
// Skip node_modules and hidden directories
|
|
126
|
+
if (entry.name !== 'node_modules' && !entry.name.startsWith('.')) {
|
|
127
|
+
walk(fullPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
131
|
+
files.push(fullPath);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
walk(directory);
|
|
136
|
+
return files;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.EsmFixer = EsmFixer;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SpecDownloader = exports.K8sClientGenerator = void 0;
|
|
3
|
+
exports.EsmFixer = exports.SpecDownloader = exports.K8sClientGenerator = void 0;
|
|
4
4
|
var k8s_client_generator_1 = require("./k8s-client-generator");
|
|
5
5
|
Object.defineProperty(exports, "K8sClientGenerator", { enumerable: true, get: function () { return k8s_client_generator_1.K8sClientGenerator; } });
|
|
6
6
|
var spec_downloader_1 = require("./spec-downloader");
|
|
7
7
|
Object.defineProperty(exports, "SpecDownloader", { enumerable: true, get: function () { return spec_downloader_1.SpecDownloader; } });
|
|
8
|
+
var esm_fixer_1 = require("./esm-fixer");
|
|
9
|
+
Object.defineProperty(exports, "EsmFixer", { enumerable: true, get: function () { return esm_fixer_1.EsmFixer; } });
|
|
@@ -33,6 +33,32 @@ export interface K8sClientGeneratorOptions {
|
|
|
33
33
|
* @default false
|
|
34
34
|
*/
|
|
35
35
|
resolveReferences?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Whether to fix imports by adding .js extensions for ESM compatibility
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
fixEsmImports?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Include NestJS Swagger @ApiProperty decorators in generated models
|
|
43
|
+
* Adds @nestjs/swagger to generated package.json
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
nestJsSwagger?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Include class-validator decorators in generated models
|
|
49
|
+
* Adds class-validator to generated package.json
|
|
50
|
+
* Decorators include: @IsString, @IsNumber, @IsOptional, @Min, @Max, @IsEmail, etc.
|
|
51
|
+
* Note: Full decorator generation requires openapi-class-transformer v2.0+
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
classValidator?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Skip adding .js extensions to imports/exports
|
|
57
|
+
* Useful for bundlers like webpack/Next.js that don't want file extensions
|
|
58
|
+
* When true, imports will be: import { Foo } from './foo' instead of './foo.js'
|
|
59
|
+
* @default false
|
|
60
|
+
*/
|
|
61
|
+
skipJsExtensions?: boolean;
|
|
36
62
|
/**
|
|
37
63
|
* Request timeout for downloading spec in milliseconds
|
|
38
64
|
* @default 30000
|
|
@@ -59,4 +85,8 @@ export declare class K8sClientGenerator {
|
|
|
59
85
|
* Clean up the downloaded spec file
|
|
60
86
|
*/
|
|
61
87
|
private cleanupSpecFile;
|
|
88
|
+
/**
|
|
89
|
+
* Add decorator dependencies to generated package.json
|
|
90
|
+
*/
|
|
91
|
+
private addDecoratorDependencies;
|
|
62
92
|
}
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.K8sClientGenerator = void 0;
|
|
37
37
|
const openapi_class_transformer_1 = require("openapi-class-transformer");
|
|
38
38
|
const spec_downloader_1 = require("./spec-downloader");
|
|
39
|
+
const esm_fixer_1 = require("./esm-fixer");
|
|
39
40
|
const fs = __importStar(require("fs"));
|
|
40
41
|
const path = __importStar(require("path"));
|
|
41
42
|
const yaml = __importStar(require("js-yaml"));
|
|
@@ -47,7 +48,7 @@ class K8sClientGenerator {
|
|
|
47
48
|
* Generate TypeScript client from a remote OpenAPI spec URL
|
|
48
49
|
*/
|
|
49
50
|
async generate(options) {
|
|
50
|
-
const { specUrl, outputDir, mode = 'full', headers, templateDir, keepSpec = false, resolveReferences = false, timeout, } = options;
|
|
51
|
+
const { specUrl, outputDir, mode = 'full', headers, templateDir, keepSpec = false, resolveReferences = false, fixEsmImports = false, nestJsSwagger = false, classValidator = false, skipJsExtensions = false, timeout, } = options;
|
|
51
52
|
let specPath;
|
|
52
53
|
try {
|
|
53
54
|
// Step 1: Download the OpenAPI spec
|
|
@@ -73,6 +74,8 @@ class K8sClientGenerator {
|
|
|
73
74
|
inputSpec: jsonSpecPath,
|
|
74
75
|
outputDir,
|
|
75
76
|
modelsOnly: mode === 'models-only',
|
|
77
|
+
nestJsSwagger,
|
|
78
|
+
skipJsExtensions,
|
|
76
79
|
};
|
|
77
80
|
// Only add templateDir if it's provided
|
|
78
81
|
if (templateDir) {
|
|
@@ -80,6 +83,17 @@ class K8sClientGenerator {
|
|
|
80
83
|
}
|
|
81
84
|
const generator = new openapi_class_transformer_1.Generator(generatorOptions);
|
|
82
85
|
await generator.generate();
|
|
86
|
+
// Step 3.5: Update generated package.json with decorator dependencies
|
|
87
|
+
if (nestJsSwagger || classValidator) {
|
|
88
|
+
console.log('Step 3.5: Adding decorator dependencies to package.json...');
|
|
89
|
+
this.addDecoratorDependencies(outputDir, nestJsSwagger, classValidator);
|
|
90
|
+
}
|
|
91
|
+
// Step 4: Fix ESM imports if requested
|
|
92
|
+
if (fixEsmImports) {
|
|
93
|
+
console.log('Step 4: Fixing ESM imports...');
|
|
94
|
+
const esmFixer = new esm_fixer_1.EsmFixer();
|
|
95
|
+
await esmFixer.fixDirectory(outputDir);
|
|
96
|
+
}
|
|
83
97
|
console.log('✅ Client generation completed successfully!');
|
|
84
98
|
console.log(`📁 Generated files location: ${outputDir}`);
|
|
85
99
|
}
|
|
@@ -165,5 +179,42 @@ class K8sClientGenerator {
|
|
|
165
179
|
console.warn('Warning: Failed to cleanup spec file:', error);
|
|
166
180
|
}
|
|
167
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Add decorator dependencies to generated package.json
|
|
184
|
+
*/
|
|
185
|
+
addDecoratorDependencies(outputDir, nestJsSwagger, classValidator) {
|
|
186
|
+
const packageJsonPath = path.join(outputDir, 'package.json');
|
|
187
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
188
|
+
console.warn('Warning: package.json not found in output directory');
|
|
189
|
+
console.log('Tip: In models-only mode, add these dependencies to your project manually');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
194
|
+
// Initialize dependencies if missing
|
|
195
|
+
if (!packageJson.dependencies) {
|
|
196
|
+
packageJson.dependencies = {};
|
|
197
|
+
}
|
|
198
|
+
// Add @nestjs/swagger if enabled
|
|
199
|
+
if (nestJsSwagger) {
|
|
200
|
+
packageJson.dependencies['@nestjs/swagger'] = '^7.0.0';
|
|
201
|
+
console.log(' ✓ Added @nestjs/swagger@^7.0.0 to dependencies');
|
|
202
|
+
}
|
|
203
|
+
// Add class-validator if enabled
|
|
204
|
+
if (classValidator) {
|
|
205
|
+
packageJson.dependencies['class-validator'] = '^0.14.0';
|
|
206
|
+
packageJson.dependencies['class-transformer'] = '^0.5.1'; // Required peer dependency
|
|
207
|
+
console.log(' ✓ Added class-validator@^0.14.0 to dependencies');
|
|
208
|
+
console.log(' ✓ Added class-transformer@^0.5.1 to dependencies');
|
|
209
|
+
console.log(' Note: Full decorator generation requires openapi-class-transformer v2.0+');
|
|
210
|
+
console.log(' For now, you can manually add validation decorators to the generated models');
|
|
211
|
+
}
|
|
212
|
+
// Write back with formatting
|
|
213
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
console.warn('Warning: Failed to update package.json:', error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
168
219
|
}
|
|
169
220
|
exports.K8sClientGenerator = K8sClientGenerator;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "klasik",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "Download OpenAPI specs from remote URLs and generate TypeScript clients with class-transformer support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,14 +22,18 @@
|
|
|
22
22
|
"api-client",
|
|
23
23
|
"generator"
|
|
24
24
|
],
|
|
25
|
-
"author": "",
|
|
25
|
+
"author": "hisco",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/hisco/klasik.git"
|
|
29
|
+
},
|
|
26
30
|
"license": "MIT",
|
|
27
31
|
"dependencies": {
|
|
28
32
|
"axios": "^1.6.0",
|
|
29
33
|
"class-transformer": "^0.5.1",
|
|
30
34
|
"commander": "^11.0.0",
|
|
31
35
|
"js-yaml": "^4.1.0",
|
|
32
|
-
"openapi-class-transformer": "^1.0.
|
|
36
|
+
"openapi-class-transformer": "^1.0.13",
|
|
33
37
|
"reflect-metadata": "^0.2.2"
|
|
34
38
|
},
|
|
35
39
|
"devDependencies": {
|