klasik 1.0.26 → 1.0.28
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 +3 -1
- package/README.md +35 -8
- package/USAGE_EXAMPLES.md +6 -6
- package/dist/cli.js +4 -2
- package/dist/crd/crd-generator.d.ts +22 -1
- package/dist/crd/crd-generator.js +73 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/jsonschema/jsonschema-generator.d.ts +1 -1
- package/dist/jsonschema/jsonschema-generator.js +5 -5
- package/dist/nestjs-type-fixer.d.ts +21 -5
- package/dist/nestjs-type-fixer.js +41 -7
- package/dist/{k8s-client-generator.d.ts → openapi-client-generator.d.ts} +3 -3
- package/dist/{k8s-client-generator.js → openapi-client-generator.js} +5 -5
- package/example.md +2 -2
- package/package.json +2 -2
- package/test-crd-fix/index.ts +2 -0
- package/test-crd-fix/v1alpha1/index.ts +2 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-cluster-resource-blacklist-item.ts +83 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-cluster-resource-whitelist-item.ts +83 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-destination-service-accounts-item.ts +83 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-destinations-item.ts +83 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-namespace-resource-blacklist-item.ts +65 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-namespace-resource-whitelist-item.ts +65 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-orphaned-resources-ignore-item.ts +83 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-orphaned-resources.ts +70 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-roles-item-jwt-tokens-item.ts +85 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-roles-item.ts +124 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-signature-keys-item.ts +47 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec-sync-windows-item.ts +209 -0
- package/test-crd-fix/v1alpha1/models/app-project-spec.ts +331 -0
- package/test-crd-fix/v1alpha1/models/app-project.ts +119 -0
- package/test-crd-fix/v1alpha1/models/index.ts +16 -0
- package/test-crd-fix/v1alpha1/models/object-meta.ts +212 -0
- package/test-fix-verification/index.ts +27 -0
- package/test-fix-verification/models/config-map-args.ts +211 -0
- package/test-fix-verification/models/field-selector.ts +47 -0
- package/test-fix-verification/models/field-spec.ts +116 -0
- package/test-fix-verification/models/generator-options.ts +101 -0
- package/test-fix-verification/models/helm-chart.ts +241 -0
- package/test-fix-verification/models/image.ts +116 -0
- package/test-fix-verification/models/inventory.ts +70 -0
- package/test-fix-verification/models/kustomization-helm-globals.ts +65 -0
- package/test-fix-verification/models/kustomization-legacy-sort-options.ts +62 -0
- package/test-fix-verification/models/kustomization-sort-options-one-of.ts +75 -0
- package/test-fix-verification/models/kustomization-sort-options-one-of1.ts +52 -0
- package/test-fix-verification/models/kustomization-sort-options.ts +33 -0
- package/test-fix-verification/models/kustomization.ts +693 -0
- package/test-fix-verification/models/kvsource.ts +80 -0
- package/test-fix-verification/models/labels.ts +103 -0
- package/test-fix-verification/models/metadata.ts +98 -0
- package/test-fix-verification/models/name-args.ts +62 -0
- package/test-fix-verification/models/patch-json6902-one-of.ts +67 -0
- package/test-fix-verification/models/patch-json6902-one-of1.ts +67 -0
- package/test-fix-verification/models/patch-json6902-one-of2-value.ts +23 -0
- package/test-fix-verification/models/patch-json6902-one-of2.ts +116 -0
- package/test-fix-verification/models/patch-json6902.ts +38 -0
- package/test-fix-verification/models/patch-target.ts +116 -0
- package/test-fix-verification/models/patches-inline-patch.ts +90 -0
- package/test-fix-verification/models/patches-options.ts +62 -0
- package/test-fix-verification/models/patches-patch-path.ts +90 -0
- package/test-fix-verification/models/replacements-inline-one-of.ts +72 -0
- package/test-fix-verification/models/replacements-inline-one-of1.ts +67 -0
- package/test-fix-verification/models/replacements-inline.ts +35 -0
- package/test-fix-verification/models/replacements-path.ts +44 -0
- package/test-fix-verification/models/replacements-source-options.ts +80 -0
- package/test-fix-verification/models/replacements-source.ts +160 -0
- package/test-fix-verification/models/replacements-target-options.ts +80 -0
- package/test-fix-verification/models/replacements-target.ts +110 -0
- package/test-fix-verification/models/replicas.ts +62 -0
- package/test-fix-verification/models/secret-args.ts +229 -0
- package/test-fix-verification/models/selector.ts +155 -0
- package/test-fix-verification/models/target.ts +134 -0
- package/test-fix-verification/models/var.ts +93 -0
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
"Bash(node test-programmatic-api.js:*)",
|
|
18
18
|
"WebFetch(domain:www.schemastore.org)",
|
|
19
19
|
"WebFetch(domain:json.schemastore.org)",
|
|
20
|
-
"Bash(node dist/cli.js:*)"
|
|
20
|
+
"Bash(node dist/cli.js:*)",
|
|
21
|
+
"Bash(curl:*)",
|
|
22
|
+
"Bash(node --input-type=module -e:*)"
|
|
21
23
|
]
|
|
22
24
|
}
|
|
23
25
|
}
|
package/README.md
CHANGED
|
@@ -78,9 +78,9 @@ npx klasik download \
|
|
|
78
78
|
### Programmatic Usage
|
|
79
79
|
|
|
80
80
|
```typescript
|
|
81
|
-
import {
|
|
81
|
+
import { OpenAPIClientGenerator } from 'klasik';
|
|
82
82
|
|
|
83
|
-
const generator = new
|
|
83
|
+
const generator = new OpenAPIClientGenerator();
|
|
84
84
|
|
|
85
85
|
await generator.generate({
|
|
86
86
|
specUrl: 'https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json',
|
|
@@ -502,6 +502,9 @@ Generate TypeScript models from Kubernetes CustomResourceDefinition (CRD) YAML f
|
|
|
502
502
|
- `-H, --header <header...>` - Custom headers for HTTP requests (format: "Key: Value")
|
|
503
503
|
- `-t, --template <dir>` - Custom template directory
|
|
504
504
|
- `-k, --keep-spec` - Keep the generated OpenAPI spec files (for debugging)
|
|
505
|
+
- `--crd-kind-snake-case` - Convert CRD kind names to snake_case for folder names (default: false)
|
|
506
|
+
- When enabled: `AppProject` → `app_project`
|
|
507
|
+
- When disabled (default): `AppProject` → `AppProject`
|
|
505
508
|
- `--timeout <ms>` - Request timeout in milliseconds (default: 30000)
|
|
506
509
|
|
|
507
510
|
**Examples:**
|
|
@@ -550,6 +553,18 @@ klasik generate-crd \
|
|
|
550
553
|
--output ./generated \
|
|
551
554
|
--include-status \
|
|
552
555
|
--nestjs-swagger
|
|
556
|
+
|
|
557
|
+
# Generate with snake_case folder names
|
|
558
|
+
klasik generate-crd \
|
|
559
|
+
--url https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/crds/appproject-crd.yaml \
|
|
560
|
+
--output ./generated \
|
|
561
|
+
--crd-kind-snake-case
|
|
562
|
+
# Output structure:
|
|
563
|
+
# generated/
|
|
564
|
+
# └── app_project/ # Instead of AppProject/
|
|
565
|
+
# └── v1alpha1/
|
|
566
|
+
# └── models/
|
|
567
|
+
# └── app-project.ts
|
|
553
568
|
```
|
|
554
569
|
|
|
555
570
|
**Output Structure:**
|
|
@@ -566,7 +581,7 @@ output/
|
|
|
566
581
|
└── index.ts
|
|
567
582
|
```
|
|
568
583
|
|
|
569
|
-
For multiple CRDs:
|
|
584
|
+
For multiple CRDs (default):
|
|
570
585
|
```
|
|
571
586
|
output/
|
|
572
587
|
├── AppProject/
|
|
@@ -578,6 +593,18 @@ output/
|
|
|
578
593
|
└── index.ts
|
|
579
594
|
```
|
|
580
595
|
|
|
596
|
+
With `--crd-kind-snake-case`:
|
|
597
|
+
```
|
|
598
|
+
output/
|
|
599
|
+
├── app_project/
|
|
600
|
+
│ ├── v1alpha1/
|
|
601
|
+
│ └── index.ts
|
|
602
|
+
├── application/
|
|
603
|
+
│ ├── v1alpha1/
|
|
604
|
+
│ └── index.ts
|
|
605
|
+
└── index.ts
|
|
606
|
+
```
|
|
607
|
+
|
|
581
608
|
### `generate-jsonschema`
|
|
582
609
|
|
|
583
610
|
Generate TypeScript models from JSON Schema files.
|
|
@@ -650,12 +677,12 @@ output/
|
|
|
650
677
|
|
|
651
678
|
## Programmatic API
|
|
652
679
|
|
|
653
|
-
###
|
|
680
|
+
### OpenAPIClientGenerator
|
|
654
681
|
|
|
655
682
|
```typescript
|
|
656
|
-
import {
|
|
683
|
+
import { OpenAPIClientGenerator } from 'klasik';
|
|
657
684
|
|
|
658
|
-
const generator = new
|
|
685
|
+
const generator = new OpenAPIClientGenerator();
|
|
659
686
|
|
|
660
687
|
await generator.generate({
|
|
661
688
|
specUrl: 'https://api.example.com/openapi.json',
|
|
@@ -946,9 +973,9 @@ npx klasik generate \
|
|
|
946
973
|
### Programmatic Example
|
|
947
974
|
|
|
948
975
|
```typescript
|
|
949
|
-
import {
|
|
976
|
+
import { OpenAPIClientGenerator } from 'klasik';
|
|
950
977
|
|
|
951
|
-
await new
|
|
978
|
+
await new OpenAPIClientGenerator().generate({
|
|
952
979
|
specUrl: 'https://api.example.com/openapi.json',
|
|
953
980
|
outputDir: './client',
|
|
954
981
|
headers: { 'Authorization': 'Bearer token' },
|
package/USAGE_EXAMPLES.md
CHANGED
|
@@ -32,10 +32,10 @@ npx klasik download \
|
|
|
32
32
|
### Programmatic Examples
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
|
-
import {
|
|
35
|
+
import { OpenAPIClientGenerator } from 'klasik';
|
|
36
36
|
|
|
37
37
|
// Works with both JSON and YAML
|
|
38
|
-
await new
|
|
38
|
+
await new OpenAPIClientGenerator().generate({
|
|
39
39
|
specUrl: 'https://api.example.com/openapi.yaml',
|
|
40
40
|
outputDir: './client'
|
|
41
41
|
});
|
|
@@ -89,9 +89,9 @@ npx klasik generate \
|
|
|
89
89
|
### Programmatic Example - Authorization Token
|
|
90
90
|
|
|
91
91
|
```typescript
|
|
92
|
-
import {
|
|
92
|
+
import { OpenAPIClientGenerator } from 'klasik';
|
|
93
93
|
|
|
94
|
-
const generator = new
|
|
94
|
+
const generator = new OpenAPIClientGenerator();
|
|
95
95
|
|
|
96
96
|
await generator.generate({
|
|
97
97
|
specUrl: 'https://api.example.com/openapi.json',
|
|
@@ -126,9 +126,9 @@ npx klasik download \
|
|
|
126
126
|
### Programmatic Example - With Reference Resolution
|
|
127
127
|
|
|
128
128
|
```typescript
|
|
129
|
-
import {
|
|
129
|
+
import { OpenAPIClientGenerator } from 'klasik';
|
|
130
130
|
|
|
131
|
-
const generator = new
|
|
131
|
+
const generator = new OpenAPIClientGenerator();
|
|
132
132
|
|
|
133
133
|
await generator.generate({
|
|
134
134
|
specUrl: 'https://api.example.com/openapi.json',
|
package/dist/cli.js
CHANGED
|
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
const commander_1 = require("commander");
|
|
38
|
-
const
|
|
38
|
+
const openapi_client_generator_1 = require("./openapi-client-generator");
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const program = new commander_1.Command();
|
|
41
41
|
program
|
|
@@ -76,7 +76,7 @@ program
|
|
|
76
76
|
}
|
|
77
77
|
// Resolve output directory to absolute path
|
|
78
78
|
const outputDir = path.resolve(options.output);
|
|
79
|
-
const generator = new
|
|
79
|
+
const generator = new openapi_client_generator_1.OpenAPIClientGenerator();
|
|
80
80
|
await generator.generate({
|
|
81
81
|
specUrl: options.url,
|
|
82
82
|
outputDir,
|
|
@@ -147,6 +147,7 @@ program
|
|
|
147
147
|
.option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
|
|
148
148
|
.option('-t, --template <dir>', 'Custom template directory')
|
|
149
149
|
.option('-k, --keep-spec', 'Keep the generated OpenAPI spec files', false)
|
|
150
|
+
.option('--crd-kind-snake-case', 'Convert CRD kind names to snake_case for folder names', false)
|
|
150
151
|
.action(async (options) => {
|
|
151
152
|
try {
|
|
152
153
|
// Parse headers if provided
|
|
@@ -177,6 +178,7 @@ program
|
|
|
177
178
|
classValidator: options.classValidator,
|
|
178
179
|
templateDir: options.template,
|
|
179
180
|
keepSpec: options.keepSpec,
|
|
181
|
+
crdKindSnakeCase: options.crdKindSnakeCase,
|
|
180
182
|
});
|
|
181
183
|
process.exit(0);
|
|
182
184
|
}
|
|
@@ -54,6 +54,11 @@ export interface CRDGeneratorOptions {
|
|
|
54
54
|
* @default false
|
|
55
55
|
*/
|
|
56
56
|
keepSpec?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Convert CRD kind names to snake_case for folder names
|
|
59
|
+
* @default false
|
|
60
|
+
*/
|
|
61
|
+
crdKindSnakeCase?: boolean;
|
|
57
62
|
}
|
|
58
63
|
/**
|
|
59
64
|
* Main orchestrator for generating TypeScript models from Kubernetes CRDs
|
|
@@ -61,7 +66,7 @@ export interface CRDGeneratorOptions {
|
|
|
61
66
|
export declare class CRDGenerator {
|
|
62
67
|
private parser;
|
|
63
68
|
private converter;
|
|
64
|
-
private
|
|
69
|
+
private openapiGenerator;
|
|
65
70
|
constructor();
|
|
66
71
|
/**
|
|
67
72
|
* Main generation workflow
|
|
@@ -76,22 +81,38 @@ export declare class CRDGenerator {
|
|
|
76
81
|
* @returns Array of version directory names
|
|
77
82
|
*/
|
|
78
83
|
private processCRD;
|
|
84
|
+
/**
|
|
85
|
+
* Format CRD kind name for use as folder name
|
|
86
|
+
* @param kind The CRD kind name (e.g., "AppProject")
|
|
87
|
+
* @param useSnakeCase Whether to convert to snake_case
|
|
88
|
+
* @returns Formatted name (e.g., "app_project" if useSnakeCase, otherwise "AppProject")
|
|
89
|
+
*/
|
|
90
|
+
private formatKindName;
|
|
79
91
|
/**
|
|
80
92
|
* Generate index file for a CRD (when multiple CRDs in one file)
|
|
81
93
|
* @param crdDir CRD directory
|
|
82
94
|
* @param versions Array of version names
|
|
95
|
+
* @param skipJsExtensions Whether to skip .js extensions
|
|
83
96
|
*/
|
|
84
97
|
private generateCRDIndex;
|
|
98
|
+
/**
|
|
99
|
+
* Generate index file for a version directory (re-exports from models)
|
|
100
|
+
* @param versionDir Version directory path
|
|
101
|
+
* @param skipJsExtensions Whether to skip .js extensions in exports
|
|
102
|
+
*/
|
|
103
|
+
private generateVersionIndexFile;
|
|
85
104
|
/**
|
|
86
105
|
* Generate main index file (when multiple CRDs in one file)
|
|
87
106
|
* @param outputDir Output directory
|
|
88
107
|
* @param crdNames Array of CRD kind names
|
|
108
|
+
* @param skipJsExtensions Whether to skip .js extensions
|
|
89
109
|
*/
|
|
90
110
|
private generateMainIndex;
|
|
91
111
|
/**
|
|
92
112
|
* Generate version index file (for single CRD)
|
|
93
113
|
* @param outputDir Output directory
|
|
94
114
|
* @param versions Array of version names
|
|
115
|
+
* @param skipJsExtensions Whether to skip .js extensions
|
|
95
116
|
*/
|
|
96
117
|
private generateVersionIndex;
|
|
97
118
|
/**
|
|
@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.CRDGenerator = void 0;
|
|
40
40
|
const crd_parser_1 = require("./crd-parser");
|
|
41
41
|
const crd_to_openapi_1 = require("./crd-to-openapi");
|
|
42
|
-
const
|
|
42
|
+
const openapi_client_generator_1 = require("../openapi-client-generator");
|
|
43
43
|
const fs = __importStar(require("fs"));
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
45
|
const axios_1 = __importDefault(require("axios"));
|
|
@@ -50,14 +50,14 @@ class CRDGenerator {
|
|
|
50
50
|
constructor() {
|
|
51
51
|
this.parser = new crd_parser_1.CRDParser();
|
|
52
52
|
this.converter = new crd_to_openapi_1.CRDToOpenAPIConverter();
|
|
53
|
-
this.
|
|
53
|
+
this.openapiGenerator = new openapi_client_generator_1.OpenAPIClientGenerator();
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
56
|
* Main generation workflow
|
|
57
57
|
* @param options Generation options
|
|
58
58
|
*/
|
|
59
59
|
async generate(options) {
|
|
60
|
-
const { urls: urlsInput, outputDir, includeStatus = false, headers, timeout, fixEsmImports = false, nestJsSwagger = false, classValidator = false, skipJsExtensions = false, templateDir, keepSpec = false, } = options;
|
|
60
|
+
const { urls: urlsInput, outputDir, includeStatus = false, headers, timeout, fixEsmImports = false, nestJsSwagger = false, classValidator = false, skipJsExtensions = false, templateDir, keepSpec = false, crdKindSnakeCase = false, } = options;
|
|
61
61
|
// Normalize to array
|
|
62
62
|
const urls = Array.isArray(urlsInput) ? urlsInput : [urlsInput];
|
|
63
63
|
try {
|
|
@@ -115,8 +115,9 @@ class CRDGenerator {
|
|
|
115
115
|
for (const crd of allCRDs) {
|
|
116
116
|
console.log(`\nProcessing CRD: ${crd.spec.names.kind}`);
|
|
117
117
|
// Determine output directory structure
|
|
118
|
+
const formattedKind = this.formatKindName(crd.spec.names.kind, crdKindSnakeCase);
|
|
118
119
|
const crdOutputDir = allCRDs.length > 1
|
|
119
|
-
? path.join(outputDir,
|
|
120
|
+
? path.join(outputDir, formattedKind)
|
|
120
121
|
: outputDir;
|
|
121
122
|
const versionDirs = await this.processCRD(crd, crdOutputDir, {
|
|
122
123
|
includeStatus,
|
|
@@ -131,18 +132,19 @@ class CRDGenerator {
|
|
|
131
132
|
});
|
|
132
133
|
// Generate index file for this CRD if multiple CRDs
|
|
133
134
|
if (allCRDs.length > 1) {
|
|
134
|
-
this.generateCRDIndex(crdOutputDir, versionDirs);
|
|
135
|
+
this.generateCRDIndex(crdOutputDir, versionDirs, skipJsExtensions);
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
138
|
// Step 4: Generate main index file
|
|
138
139
|
console.log('\nGenerating index files...');
|
|
139
140
|
if (allCRDs.length > 1) {
|
|
140
|
-
|
|
141
|
+
const formattedKindNames = allCRDs.map(c => this.formatKindName(c.spec.names.kind, crdKindSnakeCase));
|
|
142
|
+
this.generateMainIndex(outputDir, formattedKindNames, skipJsExtensions);
|
|
141
143
|
}
|
|
142
144
|
else {
|
|
143
145
|
// For single CRD, generate version index
|
|
144
146
|
const versionNames = allCRDs[0].spec.versions.map(v => v.name);
|
|
145
|
-
this.generateVersionIndex(outputDir, versionNames);
|
|
147
|
+
this.generateVersionIndex(outputDir, versionNames, skipJsExtensions);
|
|
146
148
|
}
|
|
147
149
|
console.log('\n✅ CRD client generation completed successfully!');
|
|
148
150
|
console.log(`📁 Generated files location: ${outputDir}`);
|
|
@@ -183,8 +185,8 @@ class CRDGenerator {
|
|
|
183
185
|
const specPath = path.resolve(versionDir, '.openapi-spec.json');
|
|
184
186
|
fs.writeFileSync(specPath, JSON.stringify(openApiSpec, null, 2), 'utf-8');
|
|
185
187
|
try {
|
|
186
|
-
// Generate TypeScript models using
|
|
187
|
-
await this.
|
|
188
|
+
// Generate TypeScript models using OpenAPIClientGenerator
|
|
189
|
+
await this.openapiGenerator.generate({
|
|
188
190
|
specUrl: specPath,
|
|
189
191
|
outputDir: versionDir,
|
|
190
192
|
mode: 'models-only',
|
|
@@ -195,6 +197,8 @@ class CRDGenerator {
|
|
|
195
197
|
templateDir: options.templateDir,
|
|
196
198
|
keepSpec: false, // Don't keep the OpenAPI spec in the final output
|
|
197
199
|
});
|
|
200
|
+
// Generate version-level index file
|
|
201
|
+
this.generateVersionIndexFile(versionDir, options.skipJsExtensions);
|
|
198
202
|
// Clean up temp spec file if not keeping it
|
|
199
203
|
if (!options.keepSpec) {
|
|
200
204
|
if (fs.existsSync(specPath)) {
|
|
@@ -209,27 +213,79 @@ class CRDGenerator {
|
|
|
209
213
|
}
|
|
210
214
|
return versionDirs;
|
|
211
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Format CRD kind name for use as folder name
|
|
218
|
+
* @param kind The CRD kind name (e.g., "AppProject")
|
|
219
|
+
* @param useSnakeCase Whether to convert to snake_case
|
|
220
|
+
* @returns Formatted name (e.g., "app_project" if useSnakeCase, otherwise "AppProject")
|
|
221
|
+
*/
|
|
222
|
+
formatKindName(kind, useSnakeCase) {
|
|
223
|
+
if (!useSnakeCase) {
|
|
224
|
+
return kind;
|
|
225
|
+
}
|
|
226
|
+
// Convert PascalCase to snake_case
|
|
227
|
+
// AppProject -> app_project
|
|
228
|
+
// HTTPServer -> http_server
|
|
229
|
+
return kind
|
|
230
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2') // Add underscore between lowercase and uppercase
|
|
231
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1_$2') // Handle consecutive capitals (HTTPServer -> HTTP_Server)
|
|
232
|
+
.toLowerCase();
|
|
233
|
+
}
|
|
212
234
|
/**
|
|
213
235
|
* Generate index file for a CRD (when multiple CRDs in one file)
|
|
214
236
|
* @param crdDir CRD directory
|
|
215
237
|
* @param versions Array of version names
|
|
238
|
+
* @param skipJsExtensions Whether to skip .js extensions
|
|
216
239
|
*/
|
|
217
|
-
generateCRDIndex(crdDir, versions) {
|
|
240
|
+
generateCRDIndex(crdDir, versions, skipJsExtensions = false) {
|
|
241
|
+
const extension = skipJsExtensions ? '' : '.js';
|
|
218
242
|
const exports = versions
|
|
219
|
-
.map(version => `export * as ${version.replace(/\./g, '_')} from './${version}';`)
|
|
243
|
+
.map(version => `export * as ${version.replace(/\./g, '_')} from './${version}/index${extension}';`)
|
|
220
244
|
.join('\n');
|
|
221
245
|
const indexContent = `// Auto-generated index for CRD\n${exports}\n`;
|
|
222
246
|
const indexPath = path.join(crdDir, 'index.ts');
|
|
223
247
|
fs.writeFileSync(indexPath, indexContent, 'utf-8');
|
|
224
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Generate index file for a version directory (re-exports from models)
|
|
251
|
+
* @param versionDir Version directory path
|
|
252
|
+
* @param skipJsExtensions Whether to skip .js extensions in exports
|
|
253
|
+
*/
|
|
254
|
+
generateVersionIndexFile(versionDir, skipJsExtensions = false) {
|
|
255
|
+
const extension = skipJsExtensions ? '' : '.js';
|
|
256
|
+
// First, create models/index.ts if it doesn't exist
|
|
257
|
+
// OpenAPI Generator in models-only mode doesn't create this file
|
|
258
|
+
const modelsDir = path.join(versionDir, 'models');
|
|
259
|
+
const modelsIndexPath = path.join(modelsDir, 'index.ts');
|
|
260
|
+
if (!fs.existsSync(modelsIndexPath)) {
|
|
261
|
+
// Get all .ts files in models directory (excluding index.ts itself)
|
|
262
|
+
const modelFiles = fs.readdirSync(modelsDir)
|
|
263
|
+
.filter(file => file.endsWith('.ts') && file !== 'index.ts')
|
|
264
|
+
.map(file => file.replace('.ts', ''));
|
|
265
|
+
// Generate exports for each model file
|
|
266
|
+
const exports = modelFiles
|
|
267
|
+
.map(file => `export * from './${file}${extension}';`)
|
|
268
|
+
.join('\n');
|
|
269
|
+
const modelsIndexContent = `// Auto-generated models index\n${exports}\n`;
|
|
270
|
+
fs.writeFileSync(modelsIndexPath, modelsIndexContent, 'utf-8');
|
|
271
|
+
console.log(` Generated models index: ${modelsIndexPath}`);
|
|
272
|
+
}
|
|
273
|
+
// Now create version index that exports from models
|
|
274
|
+
const indexContent = `// Auto-generated version index\nexport * from './models/index${extension}';\n`;
|
|
275
|
+
const indexPath = path.join(versionDir, 'index.ts');
|
|
276
|
+
fs.writeFileSync(indexPath, indexContent, 'utf-8');
|
|
277
|
+
console.log(` Generated version index: ${indexPath}`);
|
|
278
|
+
}
|
|
225
279
|
/**
|
|
226
280
|
* Generate main index file (when multiple CRDs in one file)
|
|
227
281
|
* @param outputDir Output directory
|
|
228
282
|
* @param crdNames Array of CRD kind names
|
|
283
|
+
* @param skipJsExtensions Whether to skip .js extensions
|
|
229
284
|
*/
|
|
230
|
-
generateMainIndex(outputDir, crdNames) {
|
|
285
|
+
generateMainIndex(outputDir, crdNames, skipJsExtensions = false) {
|
|
286
|
+
const extension = skipJsExtensions ? '' : '.js';
|
|
231
287
|
const exports = crdNames
|
|
232
|
-
.map(name => `export * as ${name} from './${name}';`)
|
|
288
|
+
.map(name => `export * as ${name} from './${name}/index${extension}';`)
|
|
233
289
|
.join('\n');
|
|
234
290
|
const indexContent = `// Auto-generated main index\n${exports}\n`;
|
|
235
291
|
const indexPath = path.join(outputDir, 'index.ts');
|
|
@@ -239,10 +295,12 @@ class CRDGenerator {
|
|
|
239
295
|
* Generate version index file (for single CRD)
|
|
240
296
|
* @param outputDir Output directory
|
|
241
297
|
* @param versions Array of version names
|
|
298
|
+
* @param skipJsExtensions Whether to skip .js extensions
|
|
242
299
|
*/
|
|
243
|
-
generateVersionIndex(outputDir, versions) {
|
|
300
|
+
generateVersionIndex(outputDir, versions, skipJsExtensions = false) {
|
|
301
|
+
const extension = skipJsExtensions ? '' : '.js';
|
|
244
302
|
const exports = versions
|
|
245
|
-
.map(version => `export * as ${version.replace(/\./g, '_')} from './${version}';`)
|
|
303
|
+
.map(version => `export * as ${version.replace(/\./g, '_')} from './${version}/index${extension}';`)
|
|
246
304
|
.join('\n');
|
|
247
305
|
const indexContent = `// Auto-generated version index\n${exports}\n`;
|
|
248
306
|
const indexPath = path.join(outputDir, 'index.ts');
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { OpenAPIClientGenerator, OpenAPIClientGeneratorOptions, GenerationMode } from './openapi-client-generator';
|
|
2
2
|
export { SpecDownloader, DownloadOptions } from './spec-downloader';
|
|
3
3
|
export { EsmFixer } from './esm-fixer';
|
|
4
4
|
export { CRDGenerator, CRDGeneratorOptions } from './crd/crd-generator';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.JSONSchemaToOpenAPIConverter = exports.JSONSchemaParser = exports.JSONSchemaGenerator = exports.CRDToOpenAPIConverter = exports.CRDParser = exports.CRDGenerator = exports.EsmFixer = exports.SpecDownloader = exports.
|
|
4
|
-
var
|
|
5
|
-
Object.defineProperty(exports, "
|
|
3
|
+
exports.JSONSchemaToOpenAPIConverter = exports.JSONSchemaParser = exports.JSONSchemaGenerator = exports.CRDToOpenAPIConverter = exports.CRDParser = exports.CRDGenerator = exports.EsmFixer = exports.SpecDownloader = exports.OpenAPIClientGenerator = void 0;
|
|
4
|
+
var openapi_client_generator_1 = require("./openapi-client-generator");
|
|
5
|
+
Object.defineProperty(exports, "OpenAPIClientGenerator", { enumerable: true, get: function () { return openapi_client_generator_1.OpenAPIClientGenerator; } });
|
|
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
8
|
var esm_fixer_1 = require("./esm-fixer");
|
|
@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.JSONSchemaGenerator = void 0;
|
|
40
40
|
const jsonschema_parser_1 = require("./jsonschema-parser");
|
|
41
41
|
const jsonschema_to_openapi_1 = require("./jsonschema-to-openapi");
|
|
42
|
-
const
|
|
42
|
+
const openapi_client_generator_1 = require("../openapi-client-generator");
|
|
43
43
|
const fs = __importStar(require("fs"));
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
45
|
const axios_1 = __importDefault(require("axios"));
|
|
@@ -50,7 +50,7 @@ class JSONSchemaGenerator {
|
|
|
50
50
|
constructor() {
|
|
51
51
|
this.parser = new jsonschema_parser_1.JSONSchemaParser();
|
|
52
52
|
this.converter = new jsonschema_to_openapi_1.JSONSchemaToOpenAPIConverter();
|
|
53
|
-
this.
|
|
53
|
+
this.openapiGenerator = new openapi_client_generator_1.OpenAPIClientGenerator();
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
56
|
* Main generation workflow
|
|
@@ -122,9 +122,9 @@ class JSONSchemaGenerator {
|
|
|
122
122
|
const specPath = path.resolve(outputDir, '.openapi-spec.json');
|
|
123
123
|
fs.writeFileSync(specPath, JSON.stringify(openApiSpec, null, 2), 'utf-8');
|
|
124
124
|
try {
|
|
125
|
-
// Generate TypeScript models using
|
|
125
|
+
// Generate TypeScript models using OpenAPIClientGenerator
|
|
126
126
|
console.log(' Generating TypeScript models...');
|
|
127
|
-
await this.
|
|
127
|
+
await this.openapiGenerator.generate({
|
|
128
128
|
specUrl: specPath,
|
|
129
129
|
outputDir,
|
|
130
130
|
mode: 'models-only',
|
|
@@ -133,7 +133,7 @@ class JSONSchemaGenerator {
|
|
|
133
133
|
fixEsmImports,
|
|
134
134
|
skipJsExtensions,
|
|
135
135
|
templateDir,
|
|
136
|
-
keepSpec: false, // Don't keep the OpenAPI spec in
|
|
136
|
+
keepSpec: false, // Don't keep the OpenAPI spec in OpenAPIClientGenerator
|
|
137
137
|
});
|
|
138
138
|
// Track definition names for index generation
|
|
139
139
|
allDefinitions.push(...Object.keys(schema.definitions));
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Fixes
|
|
2
|
+
* Fixes multiple issues in NestJS @ApiProperty decorators
|
|
3
3
|
*
|
|
4
4
|
* When openapi-class-transformer generates files with @ApiProperty decorators,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* several issues can occur:
|
|
6
|
+
*
|
|
7
|
+
* 1. Primitive type casing: Uses lowercase string literals (type: 'string')
|
|
8
|
+
* instead of constructor references (type: String)
|
|
9
|
+
* 2. Unescaped backticks: Descriptions wrapped in backticks don't escape
|
|
10
|
+
* backticks within the text
|
|
11
|
+
* 3. Undefined descriptions: Some decorators have description: undefined
|
|
12
|
+
* instead of empty strings
|
|
7
13
|
*
|
|
8
14
|
* This utility transforms:
|
|
9
15
|
* - type: 'string' -> type: String
|
|
@@ -12,9 +18,11 @@
|
|
|
12
18
|
* - type: () => [string] -> type: [String]
|
|
13
19
|
* - type: () => [number] -> type: [Number]
|
|
14
20
|
* - type: () => [boolean] -> type: [Boolean]
|
|
21
|
+
* - description: `text `code` here` -> description: `text \`code\` here`
|
|
22
|
+
* - description: undefined -> description: ''
|
|
15
23
|
*
|
|
16
24
|
* IMPORTANT: Does NOT modify attributeTypeMap at the bottom of files - those
|
|
17
|
-
* metadata strings must remain
|
|
25
|
+
* metadata strings must remain unchanged.
|
|
18
26
|
*/
|
|
19
27
|
export declare class NestJsTypeFixer {
|
|
20
28
|
/**
|
|
@@ -32,9 +40,17 @@ export declare class NestJsTypeFixer {
|
|
|
32
40
|
*/
|
|
33
41
|
private fixPrimitiveTypes;
|
|
34
42
|
/**
|
|
35
|
-
* Apply
|
|
43
|
+
* Apply all transformations to content
|
|
36
44
|
*/
|
|
37
45
|
private applyTransformations;
|
|
46
|
+
/**
|
|
47
|
+
* Escape backticks in description template literals
|
|
48
|
+
*/
|
|
49
|
+
private escapeBackticksInDescriptions;
|
|
50
|
+
/**
|
|
51
|
+
* Replace undefined descriptions with empty strings
|
|
52
|
+
*/
|
|
53
|
+
private fixUndefinedDescriptions;
|
|
38
54
|
/**
|
|
39
55
|
* Get all TypeScript files in a directory recursively
|
|
40
56
|
*/
|
|
@@ -37,11 +37,17 @@ exports.NestJsTypeFixer = void 0;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
/**
|
|
40
|
-
* Fixes
|
|
40
|
+
* Fixes multiple issues in NestJS @ApiProperty decorators
|
|
41
41
|
*
|
|
42
42
|
* When openapi-class-transformer generates files with @ApiProperty decorators,
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* several issues can occur:
|
|
44
|
+
*
|
|
45
|
+
* 1. Primitive type casing: Uses lowercase string literals (type: 'string')
|
|
46
|
+
* instead of constructor references (type: String)
|
|
47
|
+
* 2. Unescaped backticks: Descriptions wrapped in backticks don't escape
|
|
48
|
+
* backticks within the text
|
|
49
|
+
* 3. Undefined descriptions: Some decorators have description: undefined
|
|
50
|
+
* instead of empty strings
|
|
45
51
|
*
|
|
46
52
|
* This utility transforms:
|
|
47
53
|
* - type: 'string' -> type: String
|
|
@@ -50,16 +56,18 @@ const path = __importStar(require("path"));
|
|
|
50
56
|
* - type: () => [string] -> type: [String]
|
|
51
57
|
* - type: () => [number] -> type: [Number]
|
|
52
58
|
* - type: () => [boolean] -> type: [Boolean]
|
|
59
|
+
* - description: `text `code` here` -> description: `text \`code\` here`
|
|
60
|
+
* - description: undefined -> description: ''
|
|
53
61
|
*
|
|
54
62
|
* IMPORTANT: Does NOT modify attributeTypeMap at the bottom of files - those
|
|
55
|
-
* metadata strings must remain
|
|
63
|
+
* metadata strings must remain unchanged.
|
|
56
64
|
*/
|
|
57
65
|
class NestJsTypeFixer {
|
|
58
66
|
/**
|
|
59
67
|
* Fix all TypeScript files in a directory recursively
|
|
60
68
|
*/
|
|
61
69
|
async fixDirectory(directory) {
|
|
62
|
-
console.log('Fixing
|
|
70
|
+
console.log('Fixing @ApiProperty decorator issues...');
|
|
63
71
|
const files = this.getAllTsFiles(directory);
|
|
64
72
|
let fixedCount = 0;
|
|
65
73
|
for (const file of files) {
|
|
@@ -68,7 +76,7 @@ class NestJsTypeFixer {
|
|
|
68
76
|
fixedCount++;
|
|
69
77
|
}
|
|
70
78
|
}
|
|
71
|
-
console.log(`✅ Fixed
|
|
79
|
+
console.log(`✅ Fixed @ApiProperty issues in ${fixedCount} file(s)`);
|
|
72
80
|
}
|
|
73
81
|
/**
|
|
74
82
|
* Fix primitive types in a single file
|
|
@@ -116,7 +124,7 @@ class NestJsTypeFixer {
|
|
|
116
124
|
return fixedSafeZone + protectedZone;
|
|
117
125
|
}
|
|
118
126
|
/**
|
|
119
|
-
* Apply
|
|
127
|
+
* Apply all transformations to content
|
|
120
128
|
*/
|
|
121
129
|
applyTransformations(content) {
|
|
122
130
|
// Fix simple primitive types: type: 'string' -> type: String
|
|
@@ -129,8 +137,34 @@ class NestJsTypeFixer {
|
|
|
129
137
|
const capitalized = primitive.charAt(0).toUpperCase() + primitive.slice(1);
|
|
130
138
|
return `type: [${capitalized}]`;
|
|
131
139
|
});
|
|
140
|
+
// Fix unescaped backticks in description strings
|
|
141
|
+
content = this.escapeBackticksInDescriptions(content);
|
|
142
|
+
// Fix undefined descriptions
|
|
143
|
+
content = this.fixUndefinedDescriptions(content);
|
|
132
144
|
return content;
|
|
133
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Escape backticks in description template literals
|
|
148
|
+
*/
|
|
149
|
+
escapeBackticksInDescriptions(content) {
|
|
150
|
+
// Match "description: `" followed by anything until "`,"
|
|
151
|
+
// Uses non-greedy match (.*?) with dotall flag (s) to handle multiline
|
|
152
|
+
// This works even if there are unescaped backticks inside
|
|
153
|
+
return content.replace(/(description:\s*`)(.*?)(`,)/gs, (fullMatch, prefix, description, suffix) => {
|
|
154
|
+
// Escape any unescaped backticks in the description
|
|
155
|
+
// Uses negative lookbehind to avoid double-escaping
|
|
156
|
+
const escaped = description.replace(/(?<!\\)`/g, '\\`');
|
|
157
|
+
return prefix + escaped + suffix;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Replace undefined descriptions with empty strings
|
|
162
|
+
*/
|
|
163
|
+
fixUndefinedDescriptions(content) {
|
|
164
|
+
// Replace description: undefined with description: ''
|
|
165
|
+
// Only affects @ApiProperty decorators (split-point protects attributeTypeMap)
|
|
166
|
+
return content.replace(/description:\s*undefined/g, "description: ''");
|
|
167
|
+
}
|
|
134
168
|
/**
|
|
135
169
|
* Get all TypeScript files in a directory recursively
|
|
136
170
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type GenerationMode = 'full' | 'models-only';
|
|
2
|
-
export interface
|
|
2
|
+
export interface OpenAPIClientGeneratorOptions {
|
|
3
3
|
/**
|
|
4
4
|
* Remote URL to download the OpenAPI spec from
|
|
5
5
|
*/
|
|
@@ -65,13 +65,13 @@ export interface K8sClientGeneratorOptions {
|
|
|
65
65
|
*/
|
|
66
66
|
timeout?: number;
|
|
67
67
|
}
|
|
68
|
-
export declare class
|
|
68
|
+
export declare class OpenAPIClientGenerator {
|
|
69
69
|
private downloader;
|
|
70
70
|
constructor();
|
|
71
71
|
/**
|
|
72
72
|
* Generate TypeScript client from a remote OpenAPI spec URL
|
|
73
73
|
*/
|
|
74
|
-
generate(options:
|
|
74
|
+
generate(options: OpenAPIClientGeneratorOptions): Promise<void>;
|
|
75
75
|
/**
|
|
76
76
|
* Prepare the output directory
|
|
77
77
|
*/
|