zod-codegen 1.6.3 → 1.7.1
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/.github/workflows/ci.yml +50 -48
- package/.github/workflows/release.yml +13 -3
- package/.husky/commit-msg +1 -1
- package/.husky/pre-commit +1 -1
- package/.lintstagedrc.json +5 -1
- package/.nvmrc +1 -1
- package/.prettierrc.json +12 -5
- package/CHANGELOG.md +17 -0
- package/CONTRIBUTING.md +12 -12
- package/EXAMPLES.md +135 -57
- package/PERFORMANCE.md +4 -4
- package/README.md +87 -64
- package/SECURITY.md +1 -1
- package/dist/src/cli.js +11 -18
- package/dist/src/generator.d.ts +2 -2
- package/dist/src/generator.d.ts.map +1 -1
- package/dist/src/generator.js +5 -3
- package/dist/src/interfaces/code-generator.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.d.ts +3 -1
- package/dist/src/services/code-generator.service.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.js +236 -219
- package/dist/src/services/file-reader.service.d.ts +2 -0
- package/dist/src/services/file-reader.service.d.ts.map +1 -1
- package/dist/src/services/file-reader.service.js +25 -11
- package/dist/src/services/file-writer.service.d.ts.map +1 -1
- package/dist/src/services/file-writer.service.js +2 -2
- package/dist/src/services/import-builder.service.d.ts.map +1 -1
- package/dist/src/services/import-builder.service.js +3 -3
- package/dist/src/services/type-builder.service.d.ts.map +1 -1
- package/dist/src/types/generator-options.d.ts.map +1 -1
- package/dist/src/types/openapi.d.ts.map +1 -1
- package/dist/src/types/openapi.js +20 -20
- package/dist/src/utils/error-handler.d.ts.map +1 -1
- package/dist/src/utils/naming-convention.d.ts.map +1 -1
- package/dist/src/utils/naming-convention.js +6 -3
- package/dist/src/utils/signal-handler.d.ts.map +1 -1
- package/dist/tests/integration/cli-comprehensive.test.d.ts +2 -0
- package/dist/tests/integration/cli-comprehensive.test.d.ts.map +1 -0
- package/dist/tests/integration/cli-comprehensive.test.js +123 -0
- package/dist/tests/integration/cli.test.d.ts +2 -0
- package/dist/tests/integration/cli.test.d.ts.map +1 -0
- package/dist/tests/integration/cli.test.js +25 -0
- package/dist/tests/integration/error-scenarios.test.d.ts +2 -0
- package/dist/tests/integration/error-scenarios.test.d.ts.map +1 -0
- package/dist/tests/integration/error-scenarios.test.js +169 -0
- package/dist/tests/integration/snapshots.test.d.ts +2 -0
- package/dist/tests/integration/snapshots.test.d.ts.map +1 -0
- package/dist/tests/integration/snapshots.test.js +100 -0
- package/dist/tests/unit/code-generator-edge-cases.test.d.ts +2 -0
- package/dist/tests/unit/code-generator-edge-cases.test.d.ts.map +1 -0
- package/dist/tests/unit/code-generator-edge-cases.test.js +506 -0
- package/dist/tests/unit/code-generator.test.d.ts +2 -0
- package/dist/tests/unit/code-generator.test.d.ts.map +1 -0
- package/dist/tests/unit/code-generator.test.js +1364 -0
- package/dist/tests/unit/file-reader.test.d.ts +2 -0
- package/dist/tests/unit/file-reader.test.d.ts.map +1 -0
- package/dist/tests/unit/file-reader.test.js +153 -0
- package/dist/tests/unit/generator.test.d.ts +2 -0
- package/dist/tests/unit/generator.test.d.ts.map +1 -0
- package/dist/tests/unit/generator.test.js +119 -0
- package/dist/tests/unit/naming-convention.test.d.ts +2 -0
- package/dist/tests/unit/naming-convention.test.d.ts.map +1 -0
- package/dist/tests/unit/naming-convention.test.js +256 -0
- package/dist/tests/unit/reporter.test.d.ts +2 -0
- package/dist/tests/unit/reporter.test.d.ts.map +1 -0
- package/dist/tests/unit/reporter.test.js +44 -0
- package/dist/tests/unit/type-builder.test.d.ts +2 -0
- package/dist/tests/unit/type-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/type-builder.test.js +108 -0
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +10 -20
- package/eslint.config.mjs +38 -28
- package/examples/.gitkeep +1 -1
- package/examples/README.md +4 -2
- package/examples/petstore/README.md +18 -17
- package/examples/petstore/{type.ts → api.ts} +158 -74
- package/examples/petstore/authenticated-usage.ts +6 -4
- package/examples/petstore/basic-usage.ts +4 -3
- package/examples/petstore/error-handling-usage.ts +84 -0
- package/examples/petstore/retry-handler-usage.ts +11 -18
- package/examples/petstore/server-variables-usage.ts +10 -10
- package/examples/pokeapi/README.md +8 -8
- package/examples/pokeapi/api.ts +218 -0
- package/examples/pokeapi/basic-usage.ts +3 -2
- package/examples/pokeapi/custom-client.ts +5 -4
- package/package.json +17 -21
- package/src/cli.ts +20 -25
- package/src/generator.ts +13 -11
- package/src/interfaces/code-generator.ts +1 -1
- package/src/services/code-generator.service.ts +799 -1120
- package/src/services/file-reader.service.ts +35 -15
- package/src/services/file-writer.service.ts +7 -7
- package/src/services/import-builder.service.ts +9 -13
- package/src/services/type-builder.service.ts +8 -19
- package/src/types/generator-options.ts +1 -1
- package/src/types/openapi.ts +22 -22
- package/src/utils/error-handler.ts +2 -2
- package/src/utils/naming-convention.ts +13 -10
- package/src/utils/reporter.ts +2 -2
- package/src/utils/signal-handler.ts +7 -8
- package/tests/integration/cli-comprehensive.test.ts +53 -31
- package/tests/integration/cli.test.ts +5 -5
- package/tests/integration/error-scenarios.test.ts +20 -26
- package/tests/integration/snapshots.test.ts +19 -23
- package/tests/unit/code-generator-edge-cases.test.ts +133 -133
- package/tests/unit/code-generator.test.ts +431 -330
- package/tests/unit/file-reader.test.ts +58 -18
- package/tests/unit/generator.test.ts +30 -18
- package/tests/unit/naming-convention.test.ts +27 -27
- package/tests/unit/type-builder.test.ts +2 -2
- package/tsconfig.json +5 -3
- package/vitest.config.ts +11 -21
- package/dist/scripts/update-manifest.d.ts +0 -14
- package/dist/scripts/update-manifest.d.ts.map +0 -1
- package/dist/scripts/update-manifest.js +0 -33
- package/dist/src/assets/manifest.json +0 -5
- package/examples/pokeapi/type.ts +0 -109
- package/generated/type.ts +0 -371
- package/scripts/update-manifest.ts +0 -49
- package/src/assets/manifest.json +0 -5
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {dirname} from 'node:path';
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { OpenApiFileParserService, SyncFileReaderService } from '../../src/services/file-reader.service';
|
|
6
5
|
|
|
7
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
7
|
|
|
@@ -32,15 +31,15 @@ describe('SyncFileReaderService', () => {
|
|
|
32
31
|
});
|
|
33
32
|
|
|
34
33
|
it('should handle URLs', async () => {
|
|
35
|
-
// Mock fetch for URL test
|
|
36
34
|
const originalFetch = global.fetch;
|
|
37
35
|
global.fetch = vi.fn().mockResolvedValue({
|
|
38
36
|
ok: true,
|
|
39
|
-
text: async () => '{"openapi": "3.0.0", "info": {"title": "Test", "version": "1.0.0"}, "paths": {}}'
|
|
37
|
+
text: async () => '{"openapi": "3.0.0", "info": {"title": "Test", "version": "1.0.0"}, "paths": {}}'
|
|
40
38
|
} as Response);
|
|
41
39
|
|
|
42
40
|
const content = await reader.readFile('https://example.com/openapi.json');
|
|
43
41
|
expect(content).toBeTruthy();
|
|
42
|
+
expect(global.fetch).toHaveBeenCalledWith('https://example.com/openapi.json', { headers: {} });
|
|
44
43
|
|
|
45
44
|
global.fetch = originalFetch;
|
|
46
45
|
});
|
|
@@ -50,16 +49,57 @@ describe('SyncFileReaderService', () => {
|
|
|
50
49
|
const mockFetch = vi.fn().mockResolvedValue({
|
|
51
50
|
ok: false,
|
|
52
51
|
status: 404,
|
|
53
|
-
statusText: 'Not Found'
|
|
52
|
+
statusText: 'Not Found'
|
|
54
53
|
} as Response);
|
|
55
54
|
global.fetch = mockFetch;
|
|
56
55
|
|
|
57
|
-
await expect(reader.readFile('https://example.com/not-found.json')).rejects.toThrow();
|
|
56
|
+
await expect(reader.readFile('https://example.com/not-found.json')).rejects.toThrow('Failed to fetch');
|
|
58
57
|
|
|
59
|
-
// Verify fetch was called
|
|
60
|
-
expect(mockFetch).toHaveBeenCalledWith('https://example.com/not-found.json');
|
|
61
58
|
global.fetch = originalFetch;
|
|
62
59
|
});
|
|
60
|
+
|
|
61
|
+
it('should propagate network errors for URLs instead of falling back to readFileSync', async () => {
|
|
62
|
+
const originalFetch = global.fetch;
|
|
63
|
+
global.fetch = vi.fn().mockRejectedValue(new TypeError('fetch failed'));
|
|
64
|
+
|
|
65
|
+
await expect(reader.readFile('https://example.com/openapi.json')).rejects.toThrow('fetch failed');
|
|
66
|
+
|
|
67
|
+
global.fetch = originalFetch;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should extract basic auth credentials from URL into Authorization header', async () => {
|
|
71
|
+
const originalFetch = global.fetch;
|
|
72
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
73
|
+
ok: true,
|
|
74
|
+
text: async () => '{}'
|
|
75
|
+
} as Response);
|
|
76
|
+
global.fetch = mockFetch;
|
|
77
|
+
|
|
78
|
+
await reader.readFile('https://user:pass@example.com/openapi.json');
|
|
79
|
+
|
|
80
|
+
expect(mockFetch).toHaveBeenCalledWith('https://example.com/openapi.json', { headers: { Authorization: `Basic ${btoa('user:pass')}` } });
|
|
81
|
+
|
|
82
|
+
global.fetch = originalFetch;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should decode percent-encoded credentials from URL', async () => {
|
|
86
|
+
const originalFetch = global.fetch;
|
|
87
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
88
|
+
ok: true,
|
|
89
|
+
text: async () => '{}'
|
|
90
|
+
} as Response);
|
|
91
|
+
global.fetch = mockFetch;
|
|
92
|
+
|
|
93
|
+
await reader.readFile('https://julien%2Bstaging%40saris.ai:qwerty123@api.staging.saris.ai/api/openapi.json');
|
|
94
|
+
|
|
95
|
+
expect(mockFetch).toHaveBeenCalledWith('https://api.staging.saris.ai/api/openapi.json', { headers: { Authorization: `Basic ${btoa('julien+staging@saris.ai:qwerty123')}` } });
|
|
96
|
+
|
|
97
|
+
global.fetch = originalFetch;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should not treat non-http schemes as URLs', async () => {
|
|
101
|
+
await expect(reader.readFile('file:///etc/passwd')).rejects.toThrow();
|
|
102
|
+
});
|
|
63
103
|
});
|
|
64
104
|
});
|
|
65
105
|
|
|
@@ -74,8 +114,8 @@ describe('OpenApiFileParserService', () => {
|
|
|
74
114
|
it('should parse valid JSON OpenAPI spec', () => {
|
|
75
115
|
const jsonSpec = JSON.stringify({
|
|
76
116
|
openapi: '3.0.0',
|
|
77
|
-
info: {title: 'Test API', version: '1.0.0'},
|
|
78
|
-
paths: {}
|
|
117
|
+
info: { title: 'Test API', version: '1.0.0' },
|
|
118
|
+
paths: {}
|
|
79
119
|
});
|
|
80
120
|
|
|
81
121
|
const result = parser.parse(jsonSpec);
|
|
@@ -102,8 +142,8 @@ paths: {}
|
|
|
102
142
|
it('should parse already parsed objects', () => {
|
|
103
143
|
const spec = {
|
|
104
144
|
openapi: '3.0.0',
|
|
105
|
-
info: {title: 'Test API', version: '1.0.0'},
|
|
106
|
-
paths: {}
|
|
145
|
+
info: { title: 'Test API', version: '1.0.0' },
|
|
146
|
+
paths: {}
|
|
107
147
|
};
|
|
108
148
|
|
|
109
149
|
const result = parser.parse(spec);
|
|
@@ -114,8 +154,8 @@ paths: {}
|
|
|
114
154
|
it('should validate OpenAPI structure', () => {
|
|
115
155
|
const invalidSpec = {
|
|
116
156
|
openapi: '2.0.0', // Wrong version format
|
|
117
|
-
info: {title: 'Test API', version: '1.0.0'},
|
|
118
|
-
paths: {}
|
|
157
|
+
info: { title: 'Test API', version: '1.0.0' },
|
|
158
|
+
paths: {}
|
|
119
159
|
};
|
|
120
160
|
|
|
121
161
|
expect(() => parser.parse(invalidSpec)).toThrow();
|
|
@@ -125,7 +165,7 @@ paths: {}
|
|
|
125
165
|
const invalidSpec = {
|
|
126
166
|
openapi: '3.0.0',
|
|
127
167
|
// Missing info
|
|
128
|
-
paths: {}
|
|
168
|
+
paths: {}
|
|
129
169
|
};
|
|
130
170
|
|
|
131
171
|
expect(() => parser.parse(invalidSpec)).toThrow();
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {dirname} from 'node:path';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { Generator } from '../../src/generator';
|
|
6
|
+
import { Reporter } from '../../src/utils/reporter';
|
|
8
7
|
|
|
9
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
9
|
const testOutputDir = join(__dirname, '../../test-output');
|
|
@@ -16,21 +15,22 @@ describe('Generator', () => {
|
|
|
16
15
|
beforeEach(() => {
|
|
17
16
|
// Clean up test output directory
|
|
18
17
|
if (existsSync(testOutputDir)) {
|
|
19
|
-
rmSync(testOutputDir, {recursive: true, force: true});
|
|
18
|
+
rmSync(testOutputDir, { recursive: true, force: true });
|
|
20
19
|
}
|
|
21
|
-
|
|
20
|
+
|
|
21
|
+
mkdirSync(testOutputDir, { recursive: true });
|
|
22
22
|
|
|
23
23
|
// Create a mock reporter
|
|
24
24
|
mockReporter = {
|
|
25
25
|
log: vi.fn(),
|
|
26
|
-
error: vi.fn()
|
|
26
|
+
error: vi.fn()
|
|
27
27
|
} as unknown as Reporter;
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
afterEach(() => {
|
|
31
31
|
// Clean up test output directory
|
|
32
32
|
if (existsSync(testOutputDir)) {
|
|
33
|
-
rmSync(testOutputDir, {recursive: true, force: true});
|
|
33
|
+
rmSync(testOutputDir, { recursive: true, force: true });
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -62,7 +62,7 @@ describe('Generator', () => {
|
|
|
62
62
|
expect(mockReporter.error).not.toHaveBeenCalled();
|
|
63
63
|
|
|
64
64
|
// Verify output file was created
|
|
65
|
-
const outputFile = join(testOutputDir, '
|
|
65
|
+
const outputFile = join(testOutputDir, 'api.ts');
|
|
66
66
|
expect(existsSync(outputFile)).toBe(true);
|
|
67
67
|
|
|
68
68
|
// Verify file contains expected content
|
|
@@ -84,9 +84,8 @@ describe('Generator', () => {
|
|
|
84
84
|
it('should handle invalid OpenAPI specifications', async () => {
|
|
85
85
|
// Create a temporary invalid OpenAPI file
|
|
86
86
|
const invalidFile = join(testOutputDir, 'invalid.yaml');
|
|
87
|
-
mkdirSync(testOutputDir, {recursive: true});
|
|
88
|
-
|
|
89
|
-
const {writeFileSync} = await import('node:fs');
|
|
87
|
+
mkdirSync(testOutputDir, { recursive: true });
|
|
88
|
+
const { writeFileSync } = await import('node:fs');
|
|
90
89
|
writeFileSync(invalidFile, 'invalid: yaml: content: [unclosed');
|
|
91
90
|
|
|
92
91
|
generator = new Generator('test-app', '1.0.0', mockReporter, invalidFile, testOutputDir);
|
|
@@ -102,7 +101,7 @@ describe('Generator', () => {
|
|
|
102
101
|
|
|
103
102
|
await generator.run();
|
|
104
103
|
|
|
105
|
-
const outputFile = join(testOutputDir, '
|
|
104
|
+
const outputFile = join(testOutputDir, 'api.ts');
|
|
106
105
|
const content = readFileSync(outputFile, 'utf-8');
|
|
107
106
|
|
|
108
107
|
// Check for key components
|
|
@@ -117,7 +116,7 @@ describe('Generator', () => {
|
|
|
117
116
|
|
|
118
117
|
await generator.run();
|
|
119
118
|
|
|
120
|
-
const outputFile = join(testOutputDir, '
|
|
119
|
+
const outputFile = join(testOutputDir, 'api.ts');
|
|
121
120
|
const content = readFileSync(outputFile, 'utf-8');
|
|
122
121
|
|
|
123
122
|
expect(content).toContain('AUTOGENERATED');
|
|
@@ -125,11 +124,24 @@ describe('Generator', () => {
|
|
|
125
124
|
expect(content).toContain('eslint-disable');
|
|
126
125
|
});
|
|
127
126
|
|
|
127
|
+
it('should write to custom file path when output is a .ts path', async () => {
|
|
128
|
+
const customPath = join(testOutputDir, 'custom.ts');
|
|
129
|
+
generator = new Generator('test-app', '1.0.0', mockReporter, './samples/swagger-petstore.yaml', customPath);
|
|
130
|
+
|
|
131
|
+
const exitCode = await generator.run();
|
|
132
|
+
|
|
133
|
+
expect(exitCode).toBe(0);
|
|
134
|
+
expect(existsSync(customPath)).toBe(true);
|
|
135
|
+
expect(existsSync(join(testOutputDir, 'api.ts'))).toBe(false);
|
|
136
|
+
const content = readFileSync(customPath, 'utf-8');
|
|
137
|
+
expect(content).toMatch(/import\s*{\s*z\s*}\s*from\s*['"]zod['"]/);
|
|
138
|
+
});
|
|
139
|
+
|
|
128
140
|
it('should handle unknown error type (not Error instance)', async () => {
|
|
129
141
|
generator = new Generator('test-app', '1.0.0', mockReporter, './samples/swagger-petstore.yaml', testOutputDir);
|
|
130
142
|
|
|
131
143
|
// Mock the fileReader to throw a non-Error object
|
|
132
|
-
const {SyncFileReaderService} = await import('../../src/services/file-reader.service');
|
|
144
|
+
const { SyncFileReaderService } = await import('../../src/services/file-reader.service');
|
|
133
145
|
const originalReadFile = SyncFileReaderService.prototype.readFile;
|
|
134
146
|
SyncFileReaderService.prototype.readFile = vi.fn().mockRejectedValue('string error');
|
|
135
147
|
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import {describe, expect, it} from 'vitest';
|
|
2
|
-
import {
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { type NamingConvention, transformNamingConvention } from '../../src/utils/naming-convention';
|
|
3
3
|
|
|
4
4
|
describe('transformNamingConvention', () => {
|
|
5
5
|
describe('transform', () => {
|
|
6
|
-
const testCases:
|
|
6
|
+
const testCases: {
|
|
7
7
|
input: string;
|
|
8
8
|
convention: NamingConvention;
|
|
9
9
|
expected: string;
|
|
10
10
|
description: string;
|
|
11
|
-
}
|
|
11
|
+
}[] = [
|
|
12
12
|
// camelCase
|
|
13
13
|
{
|
|
14
14
|
input: 'get_user_by_id',
|
|
15
15
|
convention: 'camelCase',
|
|
16
16
|
expected: 'getUserById',
|
|
17
|
-
description: 'should convert snake_case to camelCase'
|
|
17
|
+
description: 'should convert snake_case to camelCase'
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
input: 'GetUserById',
|
|
21
21
|
convention: 'camelCase',
|
|
22
22
|
expected: 'getUserById',
|
|
23
|
-
description: 'should convert PascalCase to camelCase'
|
|
23
|
+
description: 'should convert PascalCase to camelCase'
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
input: 'get-user-by-id',
|
|
27
27
|
convention: 'camelCase',
|
|
28
28
|
expected: 'getUserById',
|
|
29
|
-
description: 'should convert kebab-case to camelCase'
|
|
29
|
+
description: 'should convert kebab-case to camelCase'
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
input: 'getUserById',
|
|
33
33
|
convention: 'camelCase',
|
|
34
34
|
expected: 'getUserById',
|
|
35
|
-
description: 'should keep camelCase as camelCase'
|
|
35
|
+
description: 'should keep camelCase as camelCase'
|
|
36
36
|
},
|
|
37
37
|
|
|
38
38
|
// PascalCase
|
|
@@ -40,19 +40,19 @@ describe('transformNamingConvention', () => {
|
|
|
40
40
|
input: 'get_user_by_id',
|
|
41
41
|
convention: 'PascalCase',
|
|
42
42
|
expected: 'GetUserById',
|
|
43
|
-
description: 'should convert snake_case to PascalCase'
|
|
43
|
+
description: 'should convert snake_case to PascalCase'
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
input: 'get-user-by-id',
|
|
47
47
|
convention: 'PascalCase',
|
|
48
48
|
expected: 'GetUserById',
|
|
49
|
-
description: 'should convert kebab-case to PascalCase'
|
|
49
|
+
description: 'should convert kebab-case to PascalCase'
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
input: 'getUserById',
|
|
53
53
|
convention: 'PascalCase',
|
|
54
54
|
expected: 'GetUserById',
|
|
55
|
-
description: 'should convert camelCase to PascalCase'
|
|
55
|
+
description: 'should convert camelCase to PascalCase'
|
|
56
56
|
},
|
|
57
57
|
|
|
58
58
|
// snake_case
|
|
@@ -60,19 +60,19 @@ describe('transformNamingConvention', () => {
|
|
|
60
60
|
input: 'getUserById',
|
|
61
61
|
convention: 'snake_case',
|
|
62
62
|
expected: 'get_user_by_id',
|
|
63
|
-
description: 'should convert camelCase to snake_case'
|
|
63
|
+
description: 'should convert camelCase to snake_case'
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
input: 'GetUserById',
|
|
67
67
|
convention: 'snake_case',
|
|
68
68
|
expected: 'get_user_by_id',
|
|
69
|
-
description: 'should convert PascalCase to snake_case'
|
|
69
|
+
description: 'should convert PascalCase to snake_case'
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
input: 'get-user-by-id',
|
|
73
73
|
convention: 'snake_case',
|
|
74
74
|
expected: 'get_user_by_id',
|
|
75
|
-
description: 'should convert kebab-case to snake_case'
|
|
75
|
+
description: 'should convert kebab-case to snake_case'
|
|
76
76
|
},
|
|
77
77
|
|
|
78
78
|
// kebab-case
|
|
@@ -80,19 +80,19 @@ describe('transformNamingConvention', () => {
|
|
|
80
80
|
input: 'getUserById',
|
|
81
81
|
convention: 'kebab-case',
|
|
82
82
|
expected: 'get-user-by-id',
|
|
83
|
-
description: 'should convert camelCase to kebab-case'
|
|
83
|
+
description: 'should convert camelCase to kebab-case'
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
input: 'GetUserById',
|
|
87
87
|
convention: 'kebab-case',
|
|
88
88
|
expected: 'get-user-by-id',
|
|
89
|
-
description: 'should convert PascalCase to kebab-case'
|
|
89
|
+
description: 'should convert PascalCase to kebab-case'
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
input: 'get_user_by_id',
|
|
93
93
|
convention: 'kebab-case',
|
|
94
94
|
expected: 'get-user-by-id',
|
|
95
|
-
description: 'should convert snake_case to kebab-case'
|
|
95
|
+
description: 'should convert snake_case to kebab-case'
|
|
96
96
|
},
|
|
97
97
|
|
|
98
98
|
// SCREAMING_SNAKE_CASE
|
|
@@ -100,13 +100,13 @@ describe('transformNamingConvention', () => {
|
|
|
100
100
|
input: 'getUserById',
|
|
101
101
|
convention: 'SCREAMING_SNAKE_CASE',
|
|
102
102
|
expected: 'GET_USER_BY_ID',
|
|
103
|
-
description: 'should convert camelCase to SCREAMING_SNAKE_CASE'
|
|
103
|
+
description: 'should convert camelCase to SCREAMING_SNAKE_CASE'
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
106
|
input: 'get-user-by-id',
|
|
107
107
|
convention: 'SCREAMING_SNAKE_CASE',
|
|
108
108
|
expected: 'GET_USER_BY_ID',
|
|
109
|
-
description: 'should convert kebab-case to SCREAMING_SNAKE_CASE'
|
|
109
|
+
description: 'should convert kebab-case to SCREAMING_SNAKE_CASE'
|
|
110
110
|
},
|
|
111
111
|
|
|
112
112
|
// SCREAMING-KEBAB-CASE
|
|
@@ -114,13 +114,13 @@ describe('transformNamingConvention', () => {
|
|
|
114
114
|
input: 'getUserById',
|
|
115
115
|
convention: 'SCREAMING-KEBAB-CASE',
|
|
116
116
|
expected: 'GET-USER-BY-ID',
|
|
117
|
-
description: 'should convert camelCase to SCREAMING-KEBAB-CASE'
|
|
117
|
+
description: 'should convert camelCase to SCREAMING-KEBAB-CASE'
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
input: 'get_user_by_id',
|
|
121
121
|
convention: 'SCREAMING-KEBAB-CASE',
|
|
122
122
|
expected: 'GET-USER-BY-ID',
|
|
123
|
-
description: 'should convert snake_case to SCREAMING-KEBAB-CASE'
|
|
123
|
+
description: 'should convert snake_case to SCREAMING-KEBAB-CASE'
|
|
124
124
|
},
|
|
125
125
|
|
|
126
126
|
// Edge cases
|
|
@@ -128,29 +128,29 @@ describe('transformNamingConvention', () => {
|
|
|
128
128
|
input: '',
|
|
129
129
|
convention: 'camelCase',
|
|
130
130
|
expected: '',
|
|
131
|
-
description: 'should handle empty string'
|
|
131
|
+
description: 'should handle empty string'
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
input: 'a',
|
|
135
135
|
convention: 'camelCase',
|
|
136
136
|
expected: 'a',
|
|
137
|
-
description: 'should handle single character'
|
|
137
|
+
description: 'should handle single character'
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
input: 'API',
|
|
141
141
|
convention: 'camelCase',
|
|
142
142
|
expected: 'aPI',
|
|
143
|
-
description: 'should handle all uppercase (splits at uppercase boundaries)'
|
|
143
|
+
description: 'should handle all uppercase (splits at uppercase boundaries)'
|
|
144
144
|
},
|
|
145
145
|
{
|
|
146
146
|
input: 'getUser123ById',
|
|
147
147
|
convention: 'snake_case',
|
|
148
148
|
expected: 'get_user_123_by_id',
|
|
149
|
-
description: 'should handle numbers in identifiers (splits at digit boundaries)'
|
|
150
|
-
}
|
|
149
|
+
description: 'should handle numbers in identifiers (splits at digit boundaries)'
|
|
150
|
+
}
|
|
151
151
|
];
|
|
152
152
|
|
|
153
|
-
testCases.forEach(({input, convention, expected, description}) => {
|
|
153
|
+
testCases.forEach(({ input, convention, expected, description }) => {
|
|
154
154
|
it(description, () => {
|
|
155
155
|
const result = transformNamingConvention(input, convention);
|
|
156
156
|
expect(result).toBe(expected);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {describe, expect, it
|
|
2
|
-
import {TypeScriptTypeBuilderService} from '../../src/services/type-builder.service';
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { TypeScriptTypeBuilderService } from '../../src/services/type-builder.service';
|
|
3
3
|
import * as ts from 'typescript';
|
|
4
4
|
|
|
5
5
|
describe('TypeScriptTypeBuilderService', () => {
|
package/tsconfig.json
CHANGED
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
"alwaysStrict": true,
|
|
5
5
|
"baseUrl": ".",
|
|
6
6
|
"checkJs": false,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
7
9
|
"downlevelIteration": true,
|
|
10
|
+
"emitDecoratorMetadata": true,
|
|
8
11
|
"esModuleInterop": true,
|
|
9
12
|
"exactOptionalPropertyTypes": true,
|
|
13
|
+
"experimentalDecorators": true,
|
|
10
14
|
"forceConsistentCasingInFileNames": true,
|
|
11
15
|
"incremental": true,
|
|
12
16
|
"isolatedModules": true,
|
|
13
17
|
"lib": ["ES2022", "DOM"],
|
|
14
18
|
"module": "ESNext",
|
|
15
19
|
"moduleResolution": "bundler",
|
|
16
|
-
"declaration": true,
|
|
17
|
-
"declarationMap": true,
|
|
18
20
|
"noEmit": false,
|
|
19
21
|
"noFallthroughCasesInSwitch": true,
|
|
20
22
|
"noImplicitAny": true,
|
|
@@ -41,6 +43,6 @@
|
|
|
41
43
|
"useUnknownInCatchVariables": true,
|
|
42
44
|
"verbatimModuleSyntax": false
|
|
43
45
|
},
|
|
44
|
-
"exclude": ["node_modules", "dist", "build", "examples
|
|
46
|
+
"exclude": ["node_modules", "dist", "build", "examples"],
|
|
45
47
|
"include": ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts", "vitest.config.ts"]
|
|
46
48
|
}
|
package/vitest.config.ts
CHANGED
|
@@ -1,39 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { defineConfig } from 'vitest/config';
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
test: {
|
|
6
6
|
globals: true,
|
|
7
7
|
environment: 'node',
|
|
8
|
-
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts
|
|
8
|
+
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts}'],
|
|
9
9
|
exclude: ['node_modules', 'dist', 'coverage'],
|
|
10
10
|
coverage: {
|
|
11
11
|
provider: 'v8',
|
|
12
12
|
reporter: ['text', 'json', 'html', 'lcov'],
|
|
13
|
-
exclude: [
|
|
14
|
-
'coverage/**',
|
|
15
|
-
'dist/**',
|
|
16
|
-
'node_modules/**',
|
|
17
|
-
'**/*.d.ts',
|
|
18
|
-
'**/*.config.*',
|
|
19
|
-
'**/*.test.*',
|
|
20
|
-
'**/*.spec.*',
|
|
21
|
-
'scripts/**',
|
|
22
|
-
'samples/**',
|
|
23
|
-
],
|
|
13
|
+
exclude: ['coverage/**', 'dist/**', 'node_modules/**', '**/*.d.ts', '**/*.config.*', '**/*.test.*', '**/*.spec.*', 'scripts/**', 'samples/**'],
|
|
24
14
|
thresholds: {
|
|
25
15
|
global: {
|
|
26
16
|
branches: 80,
|
|
27
17
|
functions: 80,
|
|
28
18
|
lines: 80,
|
|
29
|
-
statements: 80
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
19
|
+
statements: 80
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
33
23
|
},
|
|
34
24
|
resolve: {
|
|
35
25
|
alias: {
|
|
36
|
-
'@': resolve(__dirname, 'src')
|
|
37
|
-
}
|
|
38
|
-
}
|
|
26
|
+
'@': resolve(__dirname, 'src')
|
|
27
|
+
}
|
|
28
|
+
}
|
|
39
29
|
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
interface PackageJson {
|
|
2
|
-
name: string;
|
|
3
|
-
version: string;
|
|
4
|
-
description: string;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Type guard for the package.json object
|
|
8
|
-
*
|
|
9
|
-
* @param input Unknown input
|
|
10
|
-
* @returns true if the input is an event object
|
|
11
|
-
*/
|
|
12
|
-
export declare function isPackageJson(input: unknown): input is PackageJson;
|
|
13
|
-
export {};
|
|
14
|
-
//# sourceMappingURL=update-manifest.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"update-manifest.d.ts","sourceRoot":"","sources":["../../scripts/update-manifest.ts"],"names":[],"mappings":"AAKA,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAclE"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { dirname, resolve } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
/**
|
|
6
|
-
* Type guard for the package.json object
|
|
7
|
-
*
|
|
8
|
-
* @param input Unknown input
|
|
9
|
-
* @returns true if the input is an event object
|
|
10
|
-
*/
|
|
11
|
-
export function isPackageJson(input) {
|
|
12
|
-
const event = z
|
|
13
|
-
.object({
|
|
14
|
-
name: z.string(),
|
|
15
|
-
version: z.string(),
|
|
16
|
-
description: z.string(),
|
|
17
|
-
})
|
|
18
|
-
.strict()
|
|
19
|
-
.catchall(z.any())
|
|
20
|
-
.required();
|
|
21
|
-
const { success } = event.safeParse(input);
|
|
22
|
-
return success;
|
|
23
|
-
}
|
|
24
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
|
-
const sourcePath = resolve(__dirname, '..', 'package.json');
|
|
26
|
-
const data = JSON.parse(readFileSync(sourcePath, 'utf8'));
|
|
27
|
-
if (!isPackageJson(data)) {
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
const { name, version, description } = data;
|
|
31
|
-
const targetPath = resolve(__dirname, '..', 'src', 'assets', 'manifest.json');
|
|
32
|
-
writeFileSync(targetPath, JSON.stringify({ name, version, description }, null, 2));
|
|
33
|
-
process.exit(0);
|