klasik 1.0.17 → 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 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
@@ -95,10 +96,14 @@ Generate a TypeScript client from an OpenAPI spec (remote URL or local file).
95
96
  - `-u, --url <url>` - URL or file path to the OpenAPI spec (required)
96
97
  - Supports: `https://...`, `http://...`, `file://...`, `/absolute/path`, `./relative/path`
97
98
  - `-o, --output <dir>` - Output directory for generated client code (required)
99
+ - `-m, --mode <mode>` - Generation mode: `full` (models + APIs + config) or `models-only` (default: `full`)
100
+ - `full`: Generates complete axios client with APIs, models, and configuration
101
+ - `models-only`: Generates only model classes with class-transformer decorators (no API client code)
98
102
  - `-H, --header <header...>` - Custom headers for HTTP requests (format: "Key: Value")
99
103
  - Can be used multiple times for multiple headers
100
104
  - Perfect for authorization: `--header "Authorization: Bearer token"`
101
105
  - `-r, --resolve-refs` - Resolve and download external `$ref` references
106
+ - `--esm` - Add `.js` extensions to imports for ESM compatibility (Node.js modules)
102
107
  - `-t, --template <dir>` - Custom template directory (klasik includes enhanced TSDoc templates by default)
103
108
  - `-k, --keep-spec` - Keep the downloaded spec file after generation
104
109
  - `--timeout <ms>` - Request timeout in milliseconds for HTTP requests (default: 30000)
@@ -417,6 +422,127 @@ await new K8sClientGenerator().generate({
417
422
  });
418
423
  ```
419
424
 
425
+ ## Best Practices for Existing Projects
426
+
427
+ When integrating klasik into an existing TypeScript project, follow these best practices to avoid conflicts:
428
+
429
+ ### Generate into a Subdirectory
430
+
431
+ ```bash
432
+ # Generate models into src/generated to avoid overwriting project files
433
+ npx klasik generate \
434
+ --url https://api.example.com/openapi.yaml \
435
+ --output ./src/generated \
436
+ --mode models-only \
437
+ --resolve-refs
438
+ ```
439
+
440
+ ### Clean Up Generated Config Files
441
+
442
+ klasik generates `package.json` and `tsconfig.json` files. Remove them after generation:
443
+
444
+ ```json
445
+ // package.json
446
+ {
447
+ "scripts": {
448
+ "generate-client": "npx klasik generate --url $API_URL --output ./src/generated --mode models-only --resolve-refs && npm run cleanup-generated",
449
+ "cleanup-generated": "rm -f ./src/generated/package.json ./src/generated/tsconfig*.json ./src/generated/.openapi-generator*"
450
+ }
451
+ }
452
+ ```
453
+
454
+ ### TypeScript Configuration for class-transformer
455
+
456
+ Add these settings to your `tsconfig.json` for compatibility:
457
+
458
+ ```json
459
+ {
460
+ "compilerOptions": {
461
+ "experimentalDecorators": true,
462
+ "emitDecoratorMetadata": true,
463
+ "skipLibCheck": true // Skip type checking in node_modules (recommended for class-transformer)
464
+ }
465
+ }
466
+ ```
467
+
468
+ ### Models-Only Mode
469
+
470
+ Use `--mode models-only` when you:
471
+ - Only need data models with class-transformer decorators
472
+ - Want to use your own API client (axios, fetch, etc.)
473
+ - Are integrating into an existing project with its own HTTP layer
474
+ - Want to avoid package.json/tsconfig.json conflicts
475
+
476
+ ```bash
477
+ npx klasik generate \
478
+ --url https://api.example.com/openapi.yaml \
479
+ --output ./src/generated \
480
+ --mode models-only # ✅ Generates only models, no API client
481
+ ```
482
+
483
+ ### Full Mode
484
+
485
+ Use `--mode full` (default) when you:
486
+ - Want a complete ready-to-use axios client
487
+ - Are starting a new project from scratch
488
+ - Want automatic response transformation with class-transformer
489
+
490
+ ```bash
491
+ npx klasik generate \
492
+ --url https://api.example.com/openapi.yaml \
493
+ --output ./api-client \
494
+ --mode full # Generates complete client with APIs
495
+ ```
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
+
420
546
  ## Advanced Configuration
421
547
 
422
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
@@ -1,2 +1,3 @@
1
1
  export { K8sClientGenerator, K8sClientGeneratorOptions, GenerationMode } from './k8s-client-generator';
2
2
  export { SpecDownloader, DownloadOptions } from './spec-downloader';
3
+ export { EsmFixer } from './esm-fixer';
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klasik",
3
- "version": "1.0.17",
3
+ "version": "1.0.20",
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",