zod-codegen 1.0.0
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/ISSUE_TEMPLATE/bug_report.yml +93 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +70 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +87 -0
- package/.github/dependabot.yml +76 -0
- package/.github/workflows/ci.yml +143 -0
- package/.github/workflows/release.yml +65 -0
- package/.husky/commit-msg +2 -0
- package/.husky/pre-commit +5 -0
- package/.lintstagedrc.json +4 -0
- package/.nvmrc +1 -0
- package/.prettierrc.json +7 -0
- package/.releaserc.json +159 -0
- package/CHANGELOG.md +24 -0
- package/CONTRIBUTING.md +274 -0
- package/LICENCE +201 -0
- package/README.md +263 -0
- package/SECURITY.md +108 -0
- package/codecov.yml +29 -0
- package/commitlint.config.mjs +28 -0
- package/dist/scripts/update-manifest.js +31 -0
- package/dist/src/assets/manifest.json +5 -0
- package/dist/src/cli.js +60 -0
- package/dist/src/generator.js +55 -0
- package/dist/src/http/fetch-client.js +141 -0
- package/dist/src/interfaces/code-generator.js +1 -0
- package/dist/src/interfaces/file-reader.js +1 -0
- package/dist/src/polyfills/fetch.js +18 -0
- package/dist/src/services/code-generator.service.js +419 -0
- package/dist/src/services/file-reader.service.js +25 -0
- package/dist/src/services/file-writer.service.js +32 -0
- package/dist/src/services/import-builder.service.js +45 -0
- package/dist/src/services/type-builder.service.js +42 -0
- package/dist/src/types/http.js +10 -0
- package/dist/src/types/openapi.js +173 -0
- package/dist/src/utils/error-handler.js +11 -0
- package/dist/src/utils/execution-time.js +3 -0
- package/dist/src/utils/manifest.js +9 -0
- package/dist/src/utils/reporter.js +15 -0
- package/dist/src/utils/signal-handler.js +12 -0
- package/dist/src/utils/tty.js +3 -0
- package/dist/tests/integration/cli.test.js +25 -0
- package/dist/tests/unit/generator.test.js +29 -0
- package/dist/vitest.config.js +38 -0
- package/eslint.config.mjs +33 -0
- package/package.json +102 -0
- package/samples/openapi.json +1 -0
- package/samples/saris-openapi.json +7122 -0
- package/samples/swagger-petstore.yaml +802 -0
- package/samples/swagger-saris.yaml +3585 -0
- package/samples/test-logical.yaml +50 -0
- package/scripts/update-manifest.js +31 -0
- package/scripts/update-manifest.ts +47 -0
- package/src/assets/manifest.json +5 -0
- package/src/cli.ts +68 -0
- package/src/generator.ts +61 -0
- package/src/http/fetch-client.ts +181 -0
- package/src/interfaces/code-generator.ts +25 -0
- package/src/interfaces/file-reader.ts +15 -0
- package/src/polyfills/fetch.ts +26 -0
- package/src/services/code-generator.service.ts +775 -0
- package/src/services/file-reader.service.ts +29 -0
- package/src/services/file-writer.service.ts +36 -0
- package/src/services/import-builder.service.ts +64 -0
- package/src/services/type-builder.service.ts +77 -0
- package/src/types/http.ts +35 -0
- package/src/types/openapi.ts +202 -0
- package/src/utils/error-handler.ts +13 -0
- package/src/utils/execution-time.ts +3 -0
- package/src/utils/manifest.ts +17 -0
- package/src/utils/reporter.ts +16 -0
- package/src/utils/signal-handler.ts +14 -0
- package/src/utils/tty.ts +3 -0
- package/tests/integration/cli.test.ts +29 -0
- package/tests/unit/generator.test.ts +36 -0
- package/tsconfig.json +44 -0
- package/vitest.config.ts +39 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const IsTypeImport = z.boolean();
|
|
4
|
+
const ImportedElement = z.record(z.string(), IsTypeImport);
|
|
5
|
+
const ImportOptions = z.object({
|
|
6
|
+
defaultImport: ImportedElement.optional(),
|
|
7
|
+
namedImports: ImportedElement.optional(),
|
|
8
|
+
});
|
|
9
|
+
export class TypeScriptImportBuilderService {
|
|
10
|
+
buildImports() {
|
|
11
|
+
return [
|
|
12
|
+
this.createImport('zod', {
|
|
13
|
+
defaultImport: { z: false },
|
|
14
|
+
}),
|
|
15
|
+
this.createImport('path-to-regexp', {
|
|
16
|
+
namedImports: { compile: false },
|
|
17
|
+
}),
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
createImport(target, options) {
|
|
21
|
+
const safeOptions = ImportOptions.parse(options);
|
|
22
|
+
const [defaultImport] = Object.entries(safeOptions.defaultImport ?? {})[0] ?? [undefined, false];
|
|
23
|
+
const { success: hasDefaultImport } = z.string().safeParse(defaultImport);
|
|
24
|
+
const safeNameImports = ImportedElement.safeParse(safeOptions.namedImports);
|
|
25
|
+
const namedImportList = safeNameImports.success ? Object.entries(safeNameImports.data) : [];
|
|
26
|
+
// Create import specifiers for named imports
|
|
27
|
+
const namedImports = namedImportList.length > 0
|
|
28
|
+
? ts.factory.createNamedImports(namedImportList.map(([name, isTypeImport = false]) => {
|
|
29
|
+
return ts.factory.createImportSpecifier(isTypeImport, undefined, ts.factory.createIdentifier(name));
|
|
30
|
+
}))
|
|
31
|
+
: undefined;
|
|
32
|
+
// Check if we have any imports at all
|
|
33
|
+
const hasAnyImports = hasDefaultImport || namedImports;
|
|
34
|
+
// For side effects imports, we can pass undefined as the import clause
|
|
35
|
+
// For imports with bindings, we need to create the clause differently
|
|
36
|
+
return ts.factory.createImportDeclaration(undefined, hasAnyImports
|
|
37
|
+
? {
|
|
38
|
+
kind: ts.SyntaxKind.ImportClause,
|
|
39
|
+
isTypeOnly: false,
|
|
40
|
+
name: hasDefaultImport && defaultImport ? ts.factory.createIdentifier(defaultImport) : undefined,
|
|
41
|
+
namedBindings: namedImports,
|
|
42
|
+
}
|
|
43
|
+
: undefined, ts.factory.createStringLiteral(target, true), undefined);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
export class TypeScriptTypeBuilderService {
|
|
3
|
+
buildType(type) {
|
|
4
|
+
switch (type) {
|
|
5
|
+
case 'string':
|
|
6
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
7
|
+
case 'number':
|
|
8
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
9
|
+
case 'boolean':
|
|
10
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
11
|
+
case 'unknown':
|
|
12
|
+
default:
|
|
13
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
createProperty(name, type, isReadonly = false) {
|
|
17
|
+
const createIdentifier = name.startsWith('#') ? 'createPrivateIdentifier' : 'createIdentifier';
|
|
18
|
+
return ts.factory.createPropertyDeclaration(isReadonly ? [ts.factory.createToken(ts.SyntaxKind.ReadonlyKeyword)] : undefined, ts.factory[createIdentifier](name), undefined, this.buildType(type), undefined);
|
|
19
|
+
}
|
|
20
|
+
createParameter(name, type, defaultValue, isOptional = false) {
|
|
21
|
+
return ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier(this.sanitizeIdentifier(name)), isOptional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, typeof type === 'string' ? this.buildType(type) : type, defaultValue);
|
|
22
|
+
}
|
|
23
|
+
createGenericType(name) {
|
|
24
|
+
return ts.factory.createTypeParameterDeclaration(undefined, ts.factory.createIdentifier(name), undefined, undefined);
|
|
25
|
+
}
|
|
26
|
+
sanitizeIdentifier(name) {
|
|
27
|
+
let sanitized = name.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
28
|
+
if (/^[0-9]/.test(sanitized)) {
|
|
29
|
+
sanitized = '_' + sanitized;
|
|
30
|
+
}
|
|
31
|
+
if (sanitized.length === 0) {
|
|
32
|
+
sanitized = '_';
|
|
33
|
+
}
|
|
34
|
+
return sanitized;
|
|
35
|
+
}
|
|
36
|
+
toCamelCase(word) {
|
|
37
|
+
return word.charAt(0).toLowerCase() + word.slice(1);
|
|
38
|
+
}
|
|
39
|
+
toPascalCase(word) {
|
|
40
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const Reference = z.object({
|
|
3
|
+
$ref: z.string().optional(),
|
|
4
|
+
});
|
|
5
|
+
const BaseSchemaProperties = z.object({
|
|
6
|
+
$ref: z.string().optional(),
|
|
7
|
+
title: z.string().optional(),
|
|
8
|
+
multipleOf: z.number().positive().optional(),
|
|
9
|
+
maximum: z.number().optional(),
|
|
10
|
+
exclusiveMaximum: z.boolean().optional(),
|
|
11
|
+
minimum: z.number().optional(),
|
|
12
|
+
exclusiveMinimum: z.boolean().optional(),
|
|
13
|
+
maxLength: z.number().int().nonnegative().optional(),
|
|
14
|
+
minLength: z.number().int().nonnegative().optional(),
|
|
15
|
+
pattern: z.string().optional(),
|
|
16
|
+
maxItems: z.number().int().nonnegative().optional(),
|
|
17
|
+
minItems: z.number().int().nonnegative().optional(),
|
|
18
|
+
uniqueItems: z.boolean().optional(),
|
|
19
|
+
maxProperties: z.number().int().nonnegative().optional(),
|
|
20
|
+
minProperties: z.number().int().nonnegative().optional(),
|
|
21
|
+
required: z.array(z.string()).optional(),
|
|
22
|
+
enum: z.array(z.unknown()).optional(),
|
|
23
|
+
type: z.string().optional(),
|
|
24
|
+
allOf: z.array(z.unknown()).optional(),
|
|
25
|
+
oneOf: z.array(z.unknown()).optional(),
|
|
26
|
+
anyOf: z.array(z.unknown()).optional(),
|
|
27
|
+
not: z.unknown().optional(),
|
|
28
|
+
additionalProperties: z.unknown().optional(),
|
|
29
|
+
description: z.string().optional(),
|
|
30
|
+
format: z.string().optional(),
|
|
31
|
+
default: z.unknown().optional(),
|
|
32
|
+
nullable: z.boolean().optional(),
|
|
33
|
+
discriminator: Reference.optional(),
|
|
34
|
+
readOnly: z.boolean().optional(),
|
|
35
|
+
writeOnly: z.boolean().optional(),
|
|
36
|
+
xml: z
|
|
37
|
+
.object({
|
|
38
|
+
name: z.string().optional(),
|
|
39
|
+
wrapped: z.boolean().optional(),
|
|
40
|
+
})
|
|
41
|
+
.optional(),
|
|
42
|
+
externalDocs: Reference.optional(),
|
|
43
|
+
example: z.unknown().optional(),
|
|
44
|
+
deprecated: z.boolean().optional(),
|
|
45
|
+
});
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
export const SchemaProperties = z.lazy(() => BaseSchemaProperties.extend({
|
|
48
|
+
properties: z.record(z.string(), SchemaProperties).optional(),
|
|
49
|
+
items: SchemaProperties.optional(),
|
|
50
|
+
}));
|
|
51
|
+
const ServerVariable = z.object({
|
|
52
|
+
default: z.string(),
|
|
53
|
+
description: z.string().optional(),
|
|
54
|
+
enum: z.array(z.string()).optional(),
|
|
55
|
+
});
|
|
56
|
+
const Server = z.object({
|
|
57
|
+
url: z.string().url(),
|
|
58
|
+
description: z.string().optional(),
|
|
59
|
+
variables: z.record(z.string(), ServerVariable).optional(),
|
|
60
|
+
});
|
|
61
|
+
export const Parameter = z.object({
|
|
62
|
+
$ref: z.string().optional(),
|
|
63
|
+
name: z.string(),
|
|
64
|
+
in: z.enum(['query', 'header', 'path', 'cookie']),
|
|
65
|
+
description: z.string().optional(),
|
|
66
|
+
required: z.boolean().optional(),
|
|
67
|
+
deprecated: z.boolean().optional(),
|
|
68
|
+
allowEmptyValue: z.boolean().optional(),
|
|
69
|
+
style: z.string().optional(),
|
|
70
|
+
explode: z.boolean().optional(),
|
|
71
|
+
allowReserved: z.boolean().optional(),
|
|
72
|
+
schema: SchemaProperties.optional(),
|
|
73
|
+
});
|
|
74
|
+
const ResponseHeader = z.object({
|
|
75
|
+
$ref: z.string().optional(),
|
|
76
|
+
description: z.string().optional(),
|
|
77
|
+
required: z.boolean().optional(),
|
|
78
|
+
deprecated: z.boolean().optional(),
|
|
79
|
+
allowEmptyValue: z.boolean().optional(),
|
|
80
|
+
style: z.string().optional(),
|
|
81
|
+
explode: z.boolean().optional(),
|
|
82
|
+
allowReserved: z.boolean().optional(),
|
|
83
|
+
schema: Reference.optional(),
|
|
84
|
+
});
|
|
85
|
+
const MediaType = z.object({
|
|
86
|
+
schema: z.unknown().optional(),
|
|
87
|
+
});
|
|
88
|
+
export const Response = z.object({
|
|
89
|
+
$ref: z.string().optional(),
|
|
90
|
+
description: z.string(),
|
|
91
|
+
headers: z.record(z.string(), ResponseHeader).optional(),
|
|
92
|
+
content: z.record(z.string(), MediaType).optional(),
|
|
93
|
+
});
|
|
94
|
+
export const RequestBody = z.object({
|
|
95
|
+
$ref: z.string().optional(),
|
|
96
|
+
description: z.string().optional(),
|
|
97
|
+
required: z.boolean().optional(),
|
|
98
|
+
content: z.record(z.string(), MediaType).optional(),
|
|
99
|
+
});
|
|
100
|
+
export const MethodSchema = z.object({
|
|
101
|
+
summary: z.string().optional(),
|
|
102
|
+
description: z.string().optional(),
|
|
103
|
+
operationId: z.string().optional(),
|
|
104
|
+
parameters: z.array(Parameter).optional(),
|
|
105
|
+
requestBody: RequestBody.optional(),
|
|
106
|
+
responses: z.record(z.string(), Response).optional(),
|
|
107
|
+
tags: z.array(z.string()).optional(),
|
|
108
|
+
deprecated: z.boolean().optional(),
|
|
109
|
+
});
|
|
110
|
+
export const PathItem = z.object({
|
|
111
|
+
$ref: z.string().optional(),
|
|
112
|
+
summary: z.string().optional(),
|
|
113
|
+
description: z.string().optional(),
|
|
114
|
+
get: MethodSchema.optional(),
|
|
115
|
+
post: MethodSchema.optional(),
|
|
116
|
+
put: MethodSchema.optional(),
|
|
117
|
+
patch: MethodSchema.optional(),
|
|
118
|
+
delete: MethodSchema.optional(),
|
|
119
|
+
head: MethodSchema.optional(),
|
|
120
|
+
options: MethodSchema.optional(),
|
|
121
|
+
trace: MethodSchema.optional(),
|
|
122
|
+
parameters: z.array(Parameter).optional(),
|
|
123
|
+
});
|
|
124
|
+
const Info = z.object({
|
|
125
|
+
title: z.string().min(1),
|
|
126
|
+
version: z.string().min(1),
|
|
127
|
+
description: z.string().optional(),
|
|
128
|
+
termsOfService: z.string().url().optional(),
|
|
129
|
+
contact: z
|
|
130
|
+
.object({
|
|
131
|
+
name: z.string().optional(),
|
|
132
|
+
email: z.string().email().optional(),
|
|
133
|
+
url: z.string().url().optional(),
|
|
134
|
+
})
|
|
135
|
+
.optional(),
|
|
136
|
+
license: z
|
|
137
|
+
.object({
|
|
138
|
+
name: z.string().min(1),
|
|
139
|
+
url: z.string().url().optional(),
|
|
140
|
+
})
|
|
141
|
+
.optional(),
|
|
142
|
+
});
|
|
143
|
+
const SecurityRequirement = z.record(z.string(), z.array(z.string()));
|
|
144
|
+
const Tag = z.object({
|
|
145
|
+
name: z.string().min(1),
|
|
146
|
+
description: z.string().optional(),
|
|
147
|
+
externalDocs: Reference.optional(),
|
|
148
|
+
});
|
|
149
|
+
const ExternalDocumentation = z.object({
|
|
150
|
+
description: z.string().optional(),
|
|
151
|
+
url: z.string().url(),
|
|
152
|
+
});
|
|
153
|
+
const Components = z.object({
|
|
154
|
+
schemas: z.record(z.string(), SchemaProperties).optional(),
|
|
155
|
+
responses: z.record(z.string(), Response).optional(),
|
|
156
|
+
parameters: z.record(z.string(), Parameter).optional(),
|
|
157
|
+
examples: z.record(z.string(), Reference).optional(),
|
|
158
|
+
requestBodies: z.record(z.string(), RequestBody).optional(),
|
|
159
|
+
headers: z.record(z.string(), ResponseHeader).optional(),
|
|
160
|
+
securitySchemes: z.record(z.string(), Reference).optional(),
|
|
161
|
+
links: z.record(z.string(), Reference).optional(),
|
|
162
|
+
callbacks: z.record(z.string(), Reference).optional(),
|
|
163
|
+
});
|
|
164
|
+
export const OpenApiSpec = z.object({
|
|
165
|
+
openapi: z.string().regex(/^3\.\d+\.\d+$/, 'OpenAPI version must be in format 3.x.x'),
|
|
166
|
+
info: Info,
|
|
167
|
+
servers: z.array(Server).optional(),
|
|
168
|
+
paths: z.record(z.string(), PathItem),
|
|
169
|
+
components: Components.optional(),
|
|
170
|
+
security: z.array(SecurityRequirement).optional(),
|
|
171
|
+
tags: z.array(Tag).optional(),
|
|
172
|
+
externalDocs: ExternalDocumentation.optional(),
|
|
173
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getExecutionTime } from './execution-time.js';
|
|
2
|
+
export const errorReceived = (process, startTime) => () => {
|
|
3
|
+
console.log(`Done after ${String(getExecutionTime(startTime))}s`);
|
|
4
|
+
process.exit(1);
|
|
5
|
+
};
|
|
6
|
+
export const handleErrors = (process, startTime) => {
|
|
7
|
+
const catchErrors = ['unhandledRejection', 'uncaughtException'];
|
|
8
|
+
catchErrors.map((event) => {
|
|
9
|
+
return process.on(event, errorReceived(process, startTime));
|
|
10
|
+
});
|
|
11
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { format } from 'node:util';
|
|
2
|
+
export class Reporter {
|
|
3
|
+
_stdout;
|
|
4
|
+
constructor(_stdout) {
|
|
5
|
+
this._stdout = _stdout;
|
|
6
|
+
this.log = this.log.bind(this);
|
|
7
|
+
this.error = this.error.bind(this);
|
|
8
|
+
}
|
|
9
|
+
log(...args) {
|
|
10
|
+
this._stdout.write(format(...args) + '\n');
|
|
11
|
+
}
|
|
12
|
+
error(...args) {
|
|
13
|
+
this._stdout.write(format(...args) + '\n');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getExecutionTime } from './execution-time.js';
|
|
2
|
+
export const signalReceived = (process, startTime, event) => () => {
|
|
3
|
+
console.log(`Done after ${String(getExecutionTime(startTime))}s`);
|
|
4
|
+
process.kill(process.pid, event);
|
|
5
|
+
process.exit(1);
|
|
6
|
+
};
|
|
7
|
+
export const handleSignals = (process, startTime) => {
|
|
8
|
+
const catchSignals = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
|
|
9
|
+
catchSignals.map((event) => {
|
|
10
|
+
return process.once(event, signalReceived(process, startTime, event));
|
|
11
|
+
});
|
|
12
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
describe('CLI Integration', () => {
|
|
5
|
+
describe('--help', () => {
|
|
6
|
+
it('should display help information', () => {
|
|
7
|
+
const result = execSync('npm run build && node ./dist/src/cli.js --help', {
|
|
8
|
+
encoding: 'utf-8',
|
|
9
|
+
cwd: resolve(__dirname, '../..'),
|
|
10
|
+
});
|
|
11
|
+
expect(result).toContain('Usage:');
|
|
12
|
+
expect(result).toContain('--input');
|
|
13
|
+
expect(result).toContain('--output');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
describe('--version', () => {
|
|
17
|
+
it('should display version information', () => {
|
|
18
|
+
const result = execSync('npm run build && node ./dist/src/cli.js --version', {
|
|
19
|
+
encoding: 'utf-8',
|
|
20
|
+
cwd: resolve(__dirname, '../..'),
|
|
21
|
+
});
|
|
22
|
+
expect(result).toMatch(/\d+\.\d+\.\d+/);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { Generator } from '../../src/generator.js';
|
|
3
|
+
describe('Generator', () => {
|
|
4
|
+
let generator;
|
|
5
|
+
let mockReporter;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
// Create a mock reporter
|
|
8
|
+
mockReporter = {
|
|
9
|
+
info: vi.fn(),
|
|
10
|
+
error: vi.fn(),
|
|
11
|
+
warn: vi.fn(),
|
|
12
|
+
success: vi.fn(),
|
|
13
|
+
};
|
|
14
|
+
generator = new Generator('test-app', '1.0.0', mockReporter, './test-input.json', './test-output');
|
|
15
|
+
});
|
|
16
|
+
describe('constructor', () => {
|
|
17
|
+
it('should create a new Generator instance', () => {
|
|
18
|
+
expect(generator).toBeInstanceOf(Generator);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe('run', () => {
|
|
22
|
+
it('should be a function', () => {
|
|
23
|
+
expect(typeof generator.run).toBe('function');
|
|
24
|
+
});
|
|
25
|
+
it('should have the run method defined', () => {
|
|
26
|
+
expect(generator).toHaveProperty('run');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
environment: 'node',
|
|
7
|
+
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
|
8
|
+
exclude: ['node_modules', 'dist', 'coverage'],
|
|
9
|
+
coverage: {
|
|
10
|
+
provider: 'v8',
|
|
11
|
+
reporter: ['text', 'json', 'html', 'lcov'],
|
|
12
|
+
exclude: [
|
|
13
|
+
'coverage/**',
|
|
14
|
+
'dist/**',
|
|
15
|
+
'node_modules/**',
|
|
16
|
+
'**/*.d.ts',
|
|
17
|
+
'**/*.config.*',
|
|
18
|
+
'**/*.test.*',
|
|
19
|
+
'**/*.spec.*',
|
|
20
|
+
'scripts/**',
|
|
21
|
+
'samples/**',
|
|
22
|
+
],
|
|
23
|
+
thresholds: {
|
|
24
|
+
global: {
|
|
25
|
+
branches: 80,
|
|
26
|
+
functions: 80,
|
|
27
|
+
lines: 80,
|
|
28
|
+
statements: 80,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
resolve: {
|
|
34
|
+
alias: {
|
|
35
|
+
'@': resolve(__dirname, 'src'),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import eslint from '@eslint/js';
|
|
2
|
+
import { defineConfig } from 'eslint/config';
|
|
3
|
+
import tseslint from 'typescript-eslint';
|
|
4
|
+
|
|
5
|
+
export default defineConfig([
|
|
6
|
+
{
|
|
7
|
+
ignores: [
|
|
8
|
+
'**/*.mjs',
|
|
9
|
+
'dist/**/*',
|
|
10
|
+
'node_modules/**/*',
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
eslint.configs.recommended,
|
|
14
|
+
tseslint.configs.strictTypeChecked,
|
|
15
|
+
tseslint.configs.stylisticTypeChecked,
|
|
16
|
+
{
|
|
17
|
+
languageOptions: {
|
|
18
|
+
parserOptions: {
|
|
19
|
+
projectService: true,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
rules: {
|
|
23
|
+
quotes: ['error', 'single', { avoidEscape: true }],
|
|
24
|
+
'sort-imports': [
|
|
25
|
+
'error',
|
|
26
|
+
{
|
|
27
|
+
ignoreCase: true,
|
|
28
|
+
ignoreDeclarationSort: true,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
]);
|
package/package.json
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "Julien Andreu <julienandreu@me.com>",
|
|
3
|
+
"bin": {
|
|
4
|
+
"zod-codegen": "dist/src/cli.js"
|
|
5
|
+
},
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/julienandreu/zod-codegen/issues"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@apidevtools/swagger-parser": "^10.1.0",
|
|
11
|
+
"debug": "^4.3.7",
|
|
12
|
+
"js-yaml": "^4.1.0",
|
|
13
|
+
"jsonpath": "^1.1.1",
|
|
14
|
+
"loud-rejection": "^2.2.0",
|
|
15
|
+
"openapi-types": "^12.1.3",
|
|
16
|
+
"openapi-typescript": "^7.4.0",
|
|
17
|
+
"path-to-regexp": "^8.2.0",
|
|
18
|
+
"prettier": "^3.3.3",
|
|
19
|
+
"typescript": "^5.7.2",
|
|
20
|
+
"url-pattern": "^1.0.3",
|
|
21
|
+
"yargs": "^18.0.0",
|
|
22
|
+
"zod": "^3.23.8"
|
|
23
|
+
},
|
|
24
|
+
"description": "A powerful TypeScript code generator that creates Zod schemas and type-safe clients from OpenAPI specifications",
|
|
25
|
+
"keywords": [
|
|
26
|
+
"zod",
|
|
27
|
+
"openapi",
|
|
28
|
+
"swagger",
|
|
29
|
+
"codegen",
|
|
30
|
+
"typescript",
|
|
31
|
+
"type-safe",
|
|
32
|
+
"api",
|
|
33
|
+
"schema",
|
|
34
|
+
"validation"
|
|
35
|
+
],
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@commitlint/cli": "^19.8.1",
|
|
38
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
39
|
+
"@eslint/js": "^9.17.0",
|
|
40
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
41
|
+
"@semantic-release/git": "^10.0.1",
|
|
42
|
+
"@types/debug": "^4.1.12",
|
|
43
|
+
"@types/jest": "^30.0.0",
|
|
44
|
+
"@types/js-yaml": "^4.0.9",
|
|
45
|
+
"@types/jsonpath": "^0.2.4",
|
|
46
|
+
"@types/node": "^22.10.2",
|
|
47
|
+
"@types/yargs": "^17.0.33",
|
|
48
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
49
|
+
"eslint": "^9.17.0",
|
|
50
|
+
"eslint-config-prettier": "^9.1.0",
|
|
51
|
+
"husky": "^9.1.7",
|
|
52
|
+
"lint-staged": "^16.1.6",
|
|
53
|
+
"semantic-release": "^24.2.8",
|
|
54
|
+
"ts-node": "^10.9.2",
|
|
55
|
+
"typescript-eslint": "^8.18.1",
|
|
56
|
+
"undici": "^6.21.0",
|
|
57
|
+
"vitest": "^3.2.4"
|
|
58
|
+
},
|
|
59
|
+
"optionalDependencies": {
|
|
60
|
+
"undici": "^6.21.0"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/julienandreu/zod-codegen",
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"name": "zod-codegen",
|
|
65
|
+
"type": "module",
|
|
66
|
+
"exports": {
|
|
67
|
+
".": {
|
|
68
|
+
"types": "./dist/src/generator-v2.d.ts",
|
|
69
|
+
"import": "./dist/src/generator-v2.js",
|
|
70
|
+
"require": "./dist/src/generator-v2.js"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"repository": {
|
|
74
|
+
"type": "git",
|
|
75
|
+
"url": "git+ssh://git@github.com/julienandreu/zod-codegen.git"
|
|
76
|
+
},
|
|
77
|
+
"engines": {
|
|
78
|
+
"node": ">=20.0.0"
|
|
79
|
+
},
|
|
80
|
+
"scripts": {
|
|
81
|
+
"build": "rm -rf dist && tsc --project tsconfig.json && cp -r src/assets dist/src/ && chmod +x ./dist/src/cli.js",
|
|
82
|
+
"build:watch": "tsc --project tsconfig.json --watch",
|
|
83
|
+
"dev": "npm run build && node ./dist/src/cli.js --input ./samples/saris-openapi.json && npm run format && npm run lint",
|
|
84
|
+
"lint": "eslint src --fix",
|
|
85
|
+
"lint:check": "eslint src",
|
|
86
|
+
"format": "prettier src --write --log-level error",
|
|
87
|
+
"format:check": "prettier src --check",
|
|
88
|
+
"type-check": "tsc -p ./tsconfig.json --noEmit",
|
|
89
|
+
"test": "vitest run",
|
|
90
|
+
"test:watch": "vitest",
|
|
91
|
+
"test:coverage": "vitest run --coverage",
|
|
92
|
+
"test:ui": "vitest --ui",
|
|
93
|
+
"manifest:update": "ts-node scripts/update-manifest.ts",
|
|
94
|
+
"prepare": "husky",
|
|
95
|
+
"prepublishOnly": "npm run build && npm run test && npm run lint:check && npm run type-check",
|
|
96
|
+
"clean": "rm -rf dist coverage node_modules/.cache",
|
|
97
|
+
"validate": "npm run type-check && npm run lint:check && npm run format:check && npm run test",
|
|
98
|
+
"release": "semantic-release",
|
|
99
|
+
"release:dry": "semantic-release --dry-run"
|
|
100
|
+
},
|
|
101
|
+
"version": "1.0.0"
|
|
102
|
+
}
|