klasik 1.0.19 → 1.0.20
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 +51 -0
- package/dist/cli.js +2 -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 +5 -0
- package/dist/k8s-client-generator.js +8 -1
- package/package.json +1 -1
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,7 @@ 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)
|
|
105
107
|
- `-t, --template <dir>` - Custom template directory (klasik includes enhanced TSDoc templates by default)
|
|
106
108
|
- `-k, --keep-spec` - Keep the downloaded spec file after generation
|
|
107
109
|
- `--timeout <ms>` - Request timeout in milliseconds for HTTP requests (default: 30000)
|
|
@@ -492,6 +494,55 @@ npx klasik generate \
|
|
|
492
494
|
--mode full # Generates complete client with APIs
|
|
493
495
|
```
|
|
494
496
|
|
|
497
|
+
### ESM (ECMAScript Modules) Support
|
|
498
|
+
|
|
499
|
+
If your project uses ECMAScript Modules (`"type": "module"` in package.json), use the `--esm` flag to automatically add `.js` extensions to all relative imports:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
# Generate ESM-compatible code
|
|
503
|
+
npx klasik generate \
|
|
504
|
+
--url https://api.example.com/openapi.yaml \
|
|
505
|
+
--output ./src/generated \
|
|
506
|
+
--mode models-only \
|
|
507
|
+
--esm # ✅ Adds .js extensions to imports
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
**What `--esm` does:**
|
|
511
|
+
- Adds `.js` extensions to all relative imports: `from './user'` → `from './user.js'`
|
|
512
|
+
- Adds `.js` extensions to all relative exports: `export * from './api'` → `export * from './api.js'`
|
|
513
|
+
- Handles directory imports: `from './models'` → `from './models/index.js'`
|
|
514
|
+
- Only affects relative imports (doesn't touch external packages like `axios`, `class-transformer`)
|
|
515
|
+
|
|
516
|
+
**Before `--esm`:**
|
|
517
|
+
```typescript
|
|
518
|
+
import { User } from './models/user';
|
|
519
|
+
import { Cluster } from '../models/cluster';
|
|
520
|
+
export * from './api/orgs-api';
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**After `--esm`:**
|
|
524
|
+
```typescript
|
|
525
|
+
import { User } from './models/user.js';
|
|
526
|
+
import { Cluster } from '../models/cluster.js';
|
|
527
|
+
export * from './api/orgs-api.js';
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
**When to use `--esm`:**
|
|
531
|
+
- Your package.json has `"type": "module"`
|
|
532
|
+
- You're using Node.js native ESM
|
|
533
|
+
- You're targeting modern JavaScript environments
|
|
534
|
+
- You want to eliminate post-processing scripts
|
|
535
|
+
|
|
536
|
+
**package.json setup for ESM:**
|
|
537
|
+
```json
|
|
538
|
+
{
|
|
539
|
+
"type": "module",
|
|
540
|
+
"scripts": {
|
|
541
|
+
"generate-client": "npx klasik generate --url $API_URL --output src/generated --mode models-only --esm"
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
495
546
|
## Advanced Configuration
|
|
496
547
|
|
|
497
548
|
### Custom Error Handling
|
package/dist/cli.js
CHANGED
|
@@ -52,6 +52,7 @@ 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)
|
|
55
56
|
.option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
|
|
56
57
|
.action(async (options) => {
|
|
57
58
|
try {
|
|
@@ -82,6 +83,7 @@ program
|
|
|
82
83
|
templateDir: options.template,
|
|
83
84
|
keepSpec: options.keepSpec,
|
|
84
85
|
resolveReferences: options.resolveRefs,
|
|
86
|
+
fixEsmImports: options.esm,
|
|
85
87
|
timeout: parseInt(options.timeout, 10),
|
|
86
88
|
});
|
|
87
89
|
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,11 @@ 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;
|
|
36
41
|
/**
|
|
37
42
|
* Request timeout for downloading spec in milliseconds
|
|
38
43
|
* @default 30000
|
|
@@ -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, timeout, } = options;
|
|
51
52
|
let specPath;
|
|
52
53
|
try {
|
|
53
54
|
// Step 1: Download the OpenAPI spec
|
|
@@ -80,6 +81,12 @@ class K8sClientGenerator {
|
|
|
80
81
|
}
|
|
81
82
|
const generator = new openapi_class_transformer_1.Generator(generatorOptions);
|
|
82
83
|
await generator.generate();
|
|
84
|
+
// Step 4: Fix ESM imports if requested
|
|
85
|
+
if (fixEsmImports) {
|
|
86
|
+
console.log('Step 4: Fixing ESM imports...');
|
|
87
|
+
const esmFixer = new esm_fixer_1.EsmFixer();
|
|
88
|
+
await esmFixer.fixDirectory(outputDir);
|
|
89
|
+
}
|
|
83
90
|
console.log('✅ Client generation completed successfully!');
|
|
84
91
|
console.log(`📁 Generated files location: ${outputDir}`);
|
|
85
92
|
}
|
package/package.json
CHANGED