@amritk/generate-parsers 0.6.3 → 0.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/dist/generators/build-schema.d.ts +9 -10
- package/dist/generators/build-schema.d.ts.map +1 -0
- package/dist/generators/build-schema.js +118 -0
- package/dist/generators/generate-default-from-pattern.d.ts +1 -0
- package/dist/generators/generate-default-from-pattern.d.ts.map +1 -0
- package/dist/generators/generate-default-from-pattern.js +33 -0
- package/dist/generators/generate-discriminated-union-validation.d.ts +1 -0
- package/dist/generators/generate-discriminated-union-validation.d.ts.map +1 -0
- package/dist/generators/generate-discriminated-union-validation.js +38 -0
- package/dist/generators/generate-enum-check.d.ts +1 -0
- package/dist/generators/generate-enum-check.d.ts.map +1 -0
- package/dist/generators/generate-enum-check.js +8 -0
- package/dist/generators/generate-files.d.ts +2 -1
- package/dist/generators/generate-files.d.ts.map +1 -0
- package/dist/generators/generate-files.js +74 -0
- package/dist/generators/generate-non-discriminated-union-validation.d.ts +1 -0
- package/dist/generators/generate-non-discriminated-union-validation.d.ts.map +1 -0
- package/dist/generators/generate-non-discriminated-union-validation.js +28 -0
- package/dist/generators/generate-parser-function.d.ts +1 -0
- package/dist/generators/generate-parser-function.d.ts.map +1 -0
- package/dist/generators/generate-parser-function.js +1070 -0
- package/dist/generators/generate-schema-checks.d.ts +1 -0
- package/dist/generators/generate-schema-checks.d.ts.map +1 -0
- package/dist/generators/generate-schema-checks.js +99 -0
- package/dist/generators/generate-strict-assertion.d.ts +1 -0
- package/dist/generators/generate-strict-assertion.d.ts.map +1 -0
- package/dist/generators/generate-strict-assertion.js +179 -0
- package/dist/generators/generate-validation-expression.d.ts +1 -0
- package/dist/generators/generate-validation-expression.d.ts.map +1 -0
- package/dist/generators/generate-validation-expression.js +267 -0
- package/dist/helpers/apply-schema-extensions.d.ts +2 -1
- package/dist/helpers/apply-schema-extensions.d.ts.map +1 -0
- package/dist/helpers/apply-schema-extensions.js +51 -0
- package/dist/helpers/collect-helpers.d.ts +1 -0
- package/dist/helpers/collect-helpers.d.ts.map +1 -0
- package/dist/helpers/collect-helpers.js +48 -0
- package/dist/helpers/collect-imports.d.ts +1 -0
- package/dist/helpers/collect-imports.d.ts.map +1 -0
- package/dist/helpers/collect-imports.js +216 -0
- package/dist/helpers/find-discriminator.d.ts +1 -0
- package/dist/helpers/find-discriminator.d.ts.map +1 -0
- package/dist/helpers/find-discriminator.js +33 -0
- package/dist/helpers/get-default-value.d.ts +1 -0
- package/dist/helpers/get-default-value.d.ts.map +1 -0
- package/dist/helpers/get-default-value.js +74 -0
- package/dist/helpers/get-discriminator-value.d.ts +1 -0
- package/dist/helpers/get-discriminator-value.d.ts.map +1 -0
- package/dist/helpers/get-discriminator-value.js +19 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -2783
- package/dist/types/schema-extensions.d.ts +1 -0
- package/dist/types/schema-extensions.d.ts.map +1 -0
- package/dist/types/schema-extensions.js +0 -0
- package/package.json +7 -7
- package/src/generators/build-schema.ts +22 -173
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
2
|
-
import type { HelpersMode } from '
|
|
3
|
-
import type { SchemaExtensions } from '
|
|
2
|
+
import type { HelpersMode } from '../helpers/collect-helpers.js';
|
|
3
|
+
import type { SchemaExtensions } from '../types/schema-extensions.js';
|
|
4
4
|
/**
|
|
5
5
|
* Represents a generated file with its filename and content.
|
|
6
6
|
*/
|
|
@@ -9,15 +9,13 @@ export type GeneratedFile = {
|
|
|
9
9
|
content: string;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
* Builds all TypeScript files from a JSON Schema by traversing
|
|
13
|
-
*
|
|
12
|
+
* Builds all TypeScript files from a JSON Schema by traversing its entire
|
|
13
|
+
* `$ref` / `$dynamicRef` graph (via the shared `@amritk/helpers/walk-ref-graph`
|
|
14
|
+
* walker, which also seeds `$dynamicAnchor` definitions and rewrites
|
|
15
|
+
* `$dynamicRef` to `$ref`).
|
|
14
16
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* 2. Extracts all $ref references from the schema
|
|
18
|
-
* 3. Extracts all definitions with $dynamicAnchor (JSON Schema 2020-12 feature)
|
|
19
|
-
* 4. Resolves each ref and generates a TypeScript file for it
|
|
20
|
-
* 5. Recursively processes nested refs until all are handled
|
|
17
|
+
* For the root schema and each reachable definition this function generates a
|
|
18
|
+
* TypeScript file, then emits an `index.ts` barrel re-exporting them all.
|
|
21
19
|
*
|
|
22
20
|
* @param rootSchema - The root JSON Schema to build from
|
|
23
21
|
* @param rootTypeName - The name for the root type (e.g., "Document")
|
|
@@ -76,3 +74,4 @@ export type GeneratedFile = {
|
|
|
76
74
|
* ```
|
|
77
75
|
*/
|
|
78
76
|
export declare const buildSchema: (rootSchema: JSONSchema, rootTypeName: string, extensions?: SchemaExtensions, typesOnly?: boolean, logWarnings?: boolean, strict?: boolean, helpersMode?: HelpersMode, helpersImportPrefix?: string, readonly?: boolean, typeSuffix?: string) => Promise<GeneratedFile[]>;
|
|
77
|
+
//# sourceMappingURL=build-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-schema.d.ts","sourceRoot":"","sources":["../../src/generators/build-schema.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAqB,MAAM,0BAA0B,CAAA;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAahE;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,eAAO,MAAM,WAAW,eACV,UAAU,gBACR,MAAM,eACP,gBAAgB,cACjB,OAAO,gBACL,OAAO,WACZ,OAAO,gBACH,WAAW,4EAIvB,OAAO,CAAC,aAAa,EAAE,CAyCzB,CAAA"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, resolve as resolvePath } from 'node:path';
|
|
4
|
+
import { generateIndexBarrel } from '@amritk/helpers/generate-index-barrel';
|
|
5
|
+
import { walkRefGraph } from '@amritk/helpers/walk-ref-graph';
|
|
6
|
+
import { applySchemaExtensions } from '../helpers/apply-schema-extensions.js';
|
|
7
|
+
import { generateFile } from './generate-files.js';
|
|
8
|
+
/** Locate the @amritk/helpers package on disk so we can copy its runtime
|
|
9
|
+
* helper source files into the generated output when in embedded mode. */
|
|
10
|
+
const readHelperSource = async (helper) => {
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const helpersPkgPath = require.resolve('@amritk/helpers/package.json');
|
|
13
|
+
const helpersRoot = dirname(helpersPkgPath);
|
|
14
|
+
return readFile(resolvePath(helpersRoot, 'src', `${helper}.ts`), 'utf-8');
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Builds all TypeScript files from a JSON Schema by traversing its entire
|
|
18
|
+
* `$ref` / `$dynamicRef` graph (via the shared `@amritk/helpers/walk-ref-graph`
|
|
19
|
+
* walker, which also seeds `$dynamicAnchor` definitions and rewrites
|
|
20
|
+
* `$dynamicRef` to `$ref`).
|
|
21
|
+
*
|
|
22
|
+
* For the root schema and each reachable definition this function generates a
|
|
23
|
+
* TypeScript file, then emits an `index.ts` barrel re-exporting them all.
|
|
24
|
+
*
|
|
25
|
+
* @param rootSchema - The root JSON Schema to build from
|
|
26
|
+
* @param rootTypeName - The name for the root type (e.g., "Document")
|
|
27
|
+
* @param extensions - Optional map of custom extension properties to add to specific definitions.
|
|
28
|
+
* Keys are definition names (matching $defs keys), values are records of extension property
|
|
29
|
+
* names to their JSON Schema definitions. Extensions are merged as optional properties before
|
|
30
|
+
* type and parser generation.
|
|
31
|
+
* @param typesOnly - When true, only generate TypeScript type definitions without parser functions.
|
|
32
|
+
* @param logWarnings - When true, the generated parsers emit a console.warn for every input key
|
|
33
|
+
* that is not declared in the schema's properties.
|
|
34
|
+
* @param strict - When true, the generated parsers throw on type/shape mismatches
|
|
35
|
+
* (wrong type, missing required property, enum/pattern/min/max violations) instead
|
|
36
|
+
* of coercing invalid input to default values.
|
|
37
|
+
* @param helpersMode - `'package'` (default) emits `import ... from '@amritk/helpers/...'`.
|
|
38
|
+
* `'embedded'` emits `import ... from './_helpers/...'` and appends the helper sources
|
|
39
|
+
* as additional `GeneratedFile` entries so the output directory is self-contained.
|
|
40
|
+
* @param helpersImportPrefix - Relative path prefix to the `_helpers/` directory in
|
|
41
|
+
* embedded mode. Defaults to `'./'`. The recursive multi-schema build passes `'../'`,
|
|
42
|
+
* `'../../'`, etc. so nested parsers can import from a single shared `_helpers/`
|
|
43
|
+
* directory while the helper sources are emitted once at the output root.
|
|
44
|
+
* @param readonly - When true, every property, array, and record in the generated type
|
|
45
|
+
* definitions is emitted as `readonly`.
|
|
46
|
+
* @param typeSuffix - Suffix appended to every type/parser name derived from a `$ref`
|
|
47
|
+
* (e.g. `'Object'` → `ContactObject`). Defaults to `''` (no suffix). The root type name
|
|
48
|
+
* is used verbatim and is not affected by this suffix.
|
|
49
|
+
* @returns An array of generated TypeScript files
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const schema = {
|
|
54
|
+
* type: "object",
|
|
55
|
+
* properties: {
|
|
56
|
+
* info: { $ref: "#/$defs/info" }
|
|
57
|
+
* },
|
|
58
|
+
* $defs: {
|
|
59
|
+
* info: {
|
|
60
|
+
* type: "object",
|
|
61
|
+
* properties: {
|
|
62
|
+
* title: { type: "string" }
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
* };
|
|
67
|
+
*
|
|
68
|
+
* const files = buildSchema(schema, "Document");
|
|
69
|
+
*
|
|
70
|
+
* // Types-only mode — no parser functions or runtime helpers included:
|
|
71
|
+
* const typesFiles = buildSchema(schema, "Document", undefined, true);
|
|
72
|
+
*
|
|
73
|
+
* // With extensions:
|
|
74
|
+
* const filesWithExtensions = buildSchema(schema, "Document", {
|
|
75
|
+
* info: {
|
|
76
|
+
* 'x-internal': { type: 'boolean' },
|
|
77
|
+
* },
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export const buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict, helpersMode = 'package', helpersImportPrefix = './', readonly = false, typeSuffix = '') => {
|
|
82
|
+
const files = [];
|
|
83
|
+
const usedHelpers = new Set();
|
|
84
|
+
walkRefGraph(rootSchema, rootTypeName, { typeSuffix }, (node) => {
|
|
85
|
+
// `index` is reserved for the barrel below, so never let a definition of
|
|
86
|
+
// that name overwrite it.
|
|
87
|
+
if (node.filename === 'index')
|
|
88
|
+
return;
|
|
89
|
+
// Extensions are keyed by definition name, which is the node's filename for
|
|
90
|
+
// both the root (the lowercased root type name) and every `$ref` target.
|
|
91
|
+
const extended = extensions ? applySchemaExtensions(node.schema, node.filename, extensions) : node.schema;
|
|
92
|
+
const result = generateFile(extended, node.typeName, {
|
|
93
|
+
typesOnly: typesOnly ?? false,
|
|
94
|
+
rootSchema: node.rootSchema,
|
|
95
|
+
helpersMode,
|
|
96
|
+
helpersImportPrefix,
|
|
97
|
+
readonly,
|
|
98
|
+
typeSuffix,
|
|
99
|
+
...(node.ref !== undefined ? { selfRef: node.ref } : {}),
|
|
100
|
+
...(logWarnings !== undefined ? { logWarnings } : {}),
|
|
101
|
+
...(strict !== undefined ? { strict } : {}),
|
|
102
|
+
});
|
|
103
|
+
files.push({ filename: `${node.filename}.ts`, content: result.content });
|
|
104
|
+
for (const helper of result.usedHelpers)
|
|
105
|
+
usedHelpers.add(helper);
|
|
106
|
+
});
|
|
107
|
+
// In embedded mode, ship the runtime helper source files alongside the parsers so
|
|
108
|
+
// the output directory is self-contained (no `@amritk/helpers` install required).
|
|
109
|
+
// typesOnly skips parser generation entirely, so no runtime helpers are needed.
|
|
110
|
+
if (helpersMode === 'embedded' && !typesOnly) {
|
|
111
|
+
for (const helper of usedHelpers) {
|
|
112
|
+
const source = await readHelperSource(helper);
|
|
113
|
+
files.push({ filename: `_helpers/${helper}.ts`, content: source });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
files.push({ filename: 'index.ts', content: generateIndexBarrel(files, { typesOnly: typesOnly ?? false }) });
|
|
117
|
+
return files;
|
|
118
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-default-from-pattern.d.ts","sourceRoot":"","sources":["../../src/generators/generate-default-from-pattern.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,0BAA0B,YAAa,MAAM,KAAG,MAAM,GAAG,IAkCrE,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a best-guess default value from a regex pattern.
|
|
3
|
+
* Recognizes common patterns and returns appropriate defaults.
|
|
4
|
+
*/
|
|
5
|
+
export const generateDefaultFromPattern = (pattern) => {
|
|
6
|
+
// Email pattern
|
|
7
|
+
if (pattern.includes('@') || (pattern.includes('[a-zA-Z0-9]') && pattern.includes('\\.'))) {
|
|
8
|
+
return '"user@example.com"';
|
|
9
|
+
}
|
|
10
|
+
// UUID pattern
|
|
11
|
+
if ((pattern.includes('[0-9a-f]') && pattern.includes('{8}')) || pattern.includes('\\-')) {
|
|
12
|
+
if (pattern.match(/[0-9a-f].*\{8\}/i) || pattern.match(/[0-9a-fA-F]{8}/)) {
|
|
13
|
+
return '"00000000-0000-0000-0000-000000000000"';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// URL pattern
|
|
17
|
+
if (pattern.includes('https?') || pattern.includes('http')) {
|
|
18
|
+
return '"https://example.com"';
|
|
19
|
+
}
|
|
20
|
+
// Semver pattern (3.1.x, x.y.z, etc)
|
|
21
|
+
if (pattern.match(/\d.*\\\.\.*\d/)) {
|
|
22
|
+
return '"1.0.0"';
|
|
23
|
+
}
|
|
24
|
+
// ISO date pattern
|
|
25
|
+
if (pattern.includes('\\d{4}') && pattern.includes('\\d{2}')) {
|
|
26
|
+
return '"2000-01-01"';
|
|
27
|
+
}
|
|
28
|
+
// Phone number patterns
|
|
29
|
+
if (pattern.includes('\\d{3}') && (pattern.includes('\\d{4}') || pattern.includes('\\d{7}'))) {
|
|
30
|
+
return '"+1234567890"';
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
@@ -4,3 +4,4 @@ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
|
4
4
|
* Uses a discriminator property to efficiently choose the correct schema.
|
|
5
5
|
*/
|
|
6
6
|
export declare const generateDiscriminatedUnionValidation: (accessor: string, schemas: readonly JSONSchema[], discriminatorKey: string, defaultValue: string, isRequired: boolean) => string;
|
|
7
|
+
//# sourceMappingURL=generate-discriminated-union-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-discriminated-union-validation.d.ts","sourceRoot":"","sources":["../../src/generators/generate-discriminated-union-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAMjE;;;GAGG;AACH,eAAO,MAAM,oCAAoC,aACrC,MAAM,WACP,SAAS,UAAU,EAAE,oBACZ,MAAM,gBACV,MAAM,cACR,OAAO,KAClB,MAqCF,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getDefaultValue } from '../helpers/get-default-value.js';
|
|
2
|
+
import { getDiscriminatorValue } from '../helpers/get-discriminator-value.js';
|
|
3
|
+
import { generateSchemaChecks } from './generate-schema-checks.js';
|
|
4
|
+
/**
|
|
5
|
+
* Generates a discriminated union validation expression.
|
|
6
|
+
* Uses a discriminator property to efficiently choose the correct schema.
|
|
7
|
+
*/
|
|
8
|
+
export const generateDiscriminatedUnionValidation = (accessor, schemas, discriminatorKey, defaultValue, isRequired) => {
|
|
9
|
+
const cases = [];
|
|
10
|
+
for (const schema of schemas) {
|
|
11
|
+
const discriminatorValue = getDiscriminatorValue(schema, discriminatorKey);
|
|
12
|
+
if (discriminatorValue !== null) {
|
|
13
|
+
const schemaDefault = getDefaultValue(schema);
|
|
14
|
+
const checks = generateSchemaChecks(accessor, schema);
|
|
15
|
+
if (checks.length > 0) {
|
|
16
|
+
// checks[0] is safe: we guard with checks.length > 0 above
|
|
17
|
+
let combinedChecks = checks[0];
|
|
18
|
+
for (let i = 1; i < checks.length; i++) {
|
|
19
|
+
combinedChecks += ' && ' + checks[i];
|
|
20
|
+
}
|
|
21
|
+
cases.push(`${accessor}?.${discriminatorKey} === ${JSON.stringify(discriminatorValue)} && ${combinedChecks} ? ${accessor} : ${schemaDefault}`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
cases.push(`${accessor}?.${discriminatorKey} === ${JSON.stringify(discriminatorValue)} ? ${accessor} : ${schemaDefault}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (cases.length === 0) {
|
|
29
|
+
return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
|
|
30
|
+
}
|
|
31
|
+
// Chain the cases together with ternary operators using a for loop instead of .reduce()
|
|
32
|
+
// cases[0] is safe: we guard with cases.length === 0 above
|
|
33
|
+
let result = cases[0];
|
|
34
|
+
for (let i = 1; i < cases.length; i++) {
|
|
35
|
+
result = cases[i] + ' : ' + result;
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-enum-check.d.ts","sourceRoot":"","sources":["../../src/generators/generate-enum-check.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,iBAAiB,aAAc,MAAM,cAAc,SAAS,OAAO,EAAE,KAAG,MAGpF,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates an enum validation check expression.
|
|
3
|
+
* Returns a string that checks if the value is in the allowed enum values.
|
|
4
|
+
*/
|
|
5
|
+
export const generateEnumCheck = (accessor, enumValues) => {
|
|
6
|
+
const serializedEnum = JSON.stringify(enumValues);
|
|
7
|
+
return `${serializedEnum}.includes(${accessor} as never)`;
|
|
8
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
2
|
-
import { type HelpersMode, type RuntimeHelperName } from '
|
|
2
|
+
import { type HelpersMode, type RuntimeHelperName } from '../helpers/collect-helpers.js';
|
|
3
3
|
/**
|
|
4
4
|
* Options for controlling what gets generated in a file.
|
|
5
5
|
*/
|
|
@@ -91,3 +91,4 @@ export type GeneratedFileContent = {
|
|
|
91
91
|
*/
|
|
92
92
|
export declare const generateFile: (schema: JSONSchema, typeName: string, options?: GenerateFileOptions) => GeneratedFileContent;
|
|
93
93
|
export {};
|
|
94
|
+
//# sourceMappingURL=generate-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-files.d.ts","sourceRoot":"","sources":["../../src/generators/generate-files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAA;AAKjC;;GAEG;AACH,KAAK,mBAAmB,GAAG;IACzB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7C;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAA;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IACrC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;CACrD,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,YAAY,WACf,UAAU,YACR,MAAM,YACN,mBAAmB,KAC5B,oBA+CF,CAAA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { generateTypeDefinition } from '@amritk/helpers/generate-type-definition';
|
|
2
|
+
import { collectHelpers, } from '../helpers/collect-helpers.js';
|
|
3
|
+
import { collectImports } from '../helpers/collect-imports.js';
|
|
4
|
+
import { generateParserFunction, generateShapeValidator } from './generate-parser-function.js';
|
|
5
|
+
/**
|
|
6
|
+
* Generates a complete TypeScript file from a JSON Schema.
|
|
7
|
+
*
|
|
8
|
+
* The generated file contains imports, type definition, and parser function.
|
|
9
|
+
* When typesOnly is true, only the type definition is emitted without any parser.
|
|
10
|
+
*
|
|
11
|
+
* @param schema - The JSON Schema to generate code from
|
|
12
|
+
* @param typeName - The name to use for the generated TypeScript type
|
|
13
|
+
* @param options - Optional settings to control what gets generated
|
|
14
|
+
* @returns The generated file content and the set of runtime helpers it references
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const schema = {
|
|
19
|
+
* type: "object",
|
|
20
|
+
* properties: {
|
|
21
|
+
* name: { type: "string" },
|
|
22
|
+
* age: { type: "number" }
|
|
23
|
+
* },
|
|
24
|
+
* required: ["name"]
|
|
25
|
+
* };
|
|
26
|
+
*
|
|
27
|
+
* const result = generateFile(schema, "User");
|
|
28
|
+
* // result contains imports, type User, and parseUser function
|
|
29
|
+
*
|
|
30
|
+
* const typesOnly = generateFile(schema, "User", { typesOnly: true });
|
|
31
|
+
* // typesOnly contains only type-only imports and type User (no parser)
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export const generateFile = (schema, typeName, options) => {
|
|
35
|
+
const typesOnly = options?.typesOnly === true;
|
|
36
|
+
const selfRef = options?.selfRef;
|
|
37
|
+
const rootSchema = options?.rootSchema;
|
|
38
|
+
const helpersMode = options?.helpersMode ?? 'package';
|
|
39
|
+
const typeSuffix = options?.typeSuffix ?? '';
|
|
40
|
+
const typeDefinition = generateTypeDefinition(schema, typeName, { readonly: options?.readonly ?? false, typeSuffix });
|
|
41
|
+
if (typesOnly) {
|
|
42
|
+
// In types-only mode, skip the parser function and use type-only imports
|
|
43
|
+
const imports = collectImports(schema, { typesOnly: true, selfRef, rootSchema, typeSuffix });
|
|
44
|
+
let result = '';
|
|
45
|
+
if (imports.length > 0) {
|
|
46
|
+
result += imports[0];
|
|
47
|
+
for (let i = 1; i < imports.length; i++) {
|
|
48
|
+
result += '\n' + imports[i];
|
|
49
|
+
}
|
|
50
|
+
result += '\n\n';
|
|
51
|
+
}
|
|
52
|
+
return { content: result + typeDefinition, usedHelpers: new Set() };
|
|
53
|
+
}
|
|
54
|
+
const parserFunction = generateParserFunction(schema, typeName, {
|
|
55
|
+
useRefImports: true,
|
|
56
|
+
typeSuffix,
|
|
57
|
+
...(options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {}),
|
|
58
|
+
...(options?.strict !== undefined ? { strict: options.strict } : {}),
|
|
59
|
+
});
|
|
60
|
+
const shapeValidator = generateShapeValidator(schema, typeName, true, typeSuffix);
|
|
61
|
+
const combinedFunctions = `${shapeValidator}\n\n${parserFunction}`;
|
|
62
|
+
const helpers = collectHelpers(combinedFunctions, helpersMode, options?.helpersImportPrefix);
|
|
63
|
+
const imports = [...collectImports(schema, { selfRef, rootSchema, typeSuffix }), ...helpers.imports];
|
|
64
|
+
// Build file output using string concatenation instead of array join for performance
|
|
65
|
+
let result = '';
|
|
66
|
+
if (imports.length > 0) {
|
|
67
|
+
result += imports[0];
|
|
68
|
+
for (let i = 1; i < imports.length; i++) {
|
|
69
|
+
result += '\n' + imports[i];
|
|
70
|
+
}
|
|
71
|
+
result += '\n\n';
|
|
72
|
+
}
|
|
73
|
+
return { content: result + typeDefinition + '\n\n' + combinedFunctions, usedHelpers: helpers.used };
|
|
74
|
+
};
|
|
@@ -4,3 +4,4 @@ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
|
4
4
|
* Tries each schema in order until one matches.
|
|
5
5
|
*/
|
|
6
6
|
export declare const generateNonDiscriminatedUnionValidation: (accessor: string, schemas: readonly JSONSchema[], defaultValue: string, isRequired: boolean) => string;
|
|
7
|
+
//# sourceMappingURL=generate-non-discriminated-union-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-non-discriminated-union-validation.d.ts","sourceRoot":"","sources":["../../src/generators/generate-non-discriminated-union-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAIjE;;;GAGG;AACH,eAAO,MAAM,uCAAuC,aACxC,MAAM,WACP,SAAS,UAAU,EAAE,gBAChB,MAAM,cACR,OAAO,KAClB,MA0BF,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { generateSchemaChecks } from './generate-schema-checks.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a non-discriminated union validation expression.
|
|
4
|
+
* Tries each schema in order until one matches.
|
|
5
|
+
*/
|
|
6
|
+
export const generateNonDiscriminatedUnionValidation = (accessor, schemas, defaultValue, isRequired) => {
|
|
7
|
+
const cases = [];
|
|
8
|
+
for (const schema of schemas) {
|
|
9
|
+
const checks = generateSchemaChecks(accessor, schema);
|
|
10
|
+
if (checks.length > 0) {
|
|
11
|
+
let combinedChecks = checks[0];
|
|
12
|
+
for (let i = 1; i < checks.length; i++) {
|
|
13
|
+
combinedChecks += ' && ' + checks[i];
|
|
14
|
+
}
|
|
15
|
+
cases.push('(' + combinedChecks + ')');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (cases.length === 0) {
|
|
19
|
+
return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
|
|
20
|
+
}
|
|
21
|
+
// Try each schema in order
|
|
22
|
+
let combinedCheck = cases[0];
|
|
23
|
+
for (let i = 1; i < cases.length; i++) {
|
|
24
|
+
combinedCheck += ' || ' + cases[i];
|
|
25
|
+
}
|
|
26
|
+
const finalDefault = isRequired ? defaultValue : 'undefined';
|
|
27
|
+
return `(${combinedCheck}) ? ${accessor} : ${finalDefault}`;
|
|
28
|
+
};
|
|
@@ -56,3 +56,4 @@ export declare const shapeValidatorFunctionName: (typeName: string) => string;
|
|
|
56
56
|
*/
|
|
57
57
|
export declare const generateShapeValidator: (schema: JSONSchema, typeName: string, useRefImports: boolean, suffix?: string) => string;
|
|
58
58
|
export {};
|
|
59
|
+
//# sourceMappingURL=generate-parser-function.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-parser-function.d.ts","sourceRoot":"","sources":["../../src/generators/generate-parser-function.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAMjE;;GAEG;AACH,KAAK,qBAAqB,GAAG;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAChC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAqqCD;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,WACzB,UAAU,YACR,MAAM,YACN,qBAAqB,KAC9B,MAEF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,aAAc,MAAM,KAAG,MAAsC,CAAA;AAEpG;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,WACzB,UAAU,YACR,MAAM,iBACD,OAAO,sBAErB,MAyDF,CAAA"}
|