@amritk/generate-parsers 0.23.0 → 0.25.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/README.md +37 -463
- package/dist/index.d.ts +22 -2
- package/dist/index.js +18 -1
- package/package.json +15 -40
- package/AI.md +0 -52
- package/dist/generators/assert-supported-keywords.d.ts +0 -30
- package/dist/generators/assert-supported-keywords.js +0 -87
- package/dist/generators/build-schema.d.ts +0 -111
- package/dist/generators/build-schema.js +0 -74
- package/dist/generators/generate-deep-equal-check.d.ts +0 -24
- package/dist/generators/generate-deep-equal-check.js +0 -35
- package/dist/generators/generate-default-from-pattern.d.ts +0 -26
- package/dist/generators/generate-default-from-pattern.js +0 -223
- package/dist/generators/generate-discriminated-union-validation.d.ts +0 -15
- package/dist/generators/generate-discriminated-union-validation.js +0 -28
- package/dist/generators/generate-enum-check.d.ts +0 -35
- package/dist/generators/generate-enum-check.js +0 -31
- package/dist/generators/generate-files.d.ts +0 -138
- package/dist/generators/generate-files.js +0 -79
- package/dist/generators/generate-non-discriminated-union-validation.d.ts +0 -6
- package/dist/generators/generate-non-discriminated-union-validation.js +0 -26
- package/dist/generators/generate-parser-function.d.ts +0 -120
- package/dist/generators/generate-parser-function.js +0 -1955
- package/dist/generators/generate-schema-checks.d.ts +0 -5
- package/dist/generators/generate-schema-checks.js +0 -260
- package/dist/generators/generate-strict-assertion.d.ts +0 -155
- package/dist/generators/generate-strict-assertion.js +0 -759
- package/dist/generators/generate-type-checks.d.ts +0 -148
- package/dist/generators/generate-type-checks.js +0 -454
- package/dist/generators/generate-unique-items-check.d.ts +0 -16
- package/dist/generators/generate-unique-items-check.js +0 -32
- package/dist/generators/generate-validation-expression.d.ts +0 -79
- package/dist/generators/generate-validation-expression.js +0 -416
- package/dist/generators/subschema-match.d.ts +0 -48
- package/dist/generators/subschema-match.js +0 -485
- package/dist/generators/unevaluated-match.d.ts +0 -19
- package/dist/generators/unevaluated-match.js +0 -206
- package/dist/helpers/apply-schema-extensions.d.ts +0 -35
- package/dist/helpers/apply-schema-extensions.js +0 -20
- package/dist/helpers/collect-helpers.d.ts +0 -15
- package/dist/helpers/collect-helpers.js +0 -64
- package/dist/helpers/collect-imports.d.ts +0 -93
- package/dist/helpers/collect-imports.js +0 -197
- package/dist/helpers/find-discriminator.d.ts +0 -6
- package/dist/helpers/find-discriminator.js +0 -27
- package/dist/helpers/get-default-value.d.ts +0 -7
- package/dist/helpers/get-default-value.js +0 -155
- package/dist/helpers/get-discriminator-value.d.ts +0 -6
- package/dist/helpers/get-discriminator-value.js +0 -18
- package/dist/types/schema-extensions.d.ts +0 -34
- package/dist/types/schema-extensions.js +0 -0
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import { regexLiteral } from "@amritk/helpers/escape-regex-pattern";
|
|
2
|
-
import { resolveRef } from "@amritk/helpers/resolve-ref";
|
|
3
|
-
import { isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
4
|
-
const NONE = { all: false, terms: [] };
|
|
5
|
-
const orJoin = (terms) => terms.length === 1 ? terms[0] : `(${terms.join(" || ")})`;
|
|
6
|
-
const depthOf = (ctx) => ctx.depth ?? 0;
|
|
7
|
-
const nest = (ctx) => ({ ...ctx, depth: depthOf(ctx) + 1 });
|
|
8
|
-
const conditioned = (coverage, cond) => {
|
|
9
|
-
if (coverage.all)
|
|
10
|
-
return { all: false, terms: [cond] };
|
|
11
|
-
if (coverage.terms.length === 0)
|
|
12
|
-
return NONE;
|
|
13
|
-
return { all: false, terms: [`(${cond} && ${orJoin(coverage.terms)})`] };
|
|
14
|
-
};
|
|
15
|
-
const merge = (into, from) => into.all || from.all ? { all: true, terms: [] } : { all: false, terms: [...into.terms, ...from.terms] };
|
|
16
|
-
const ownPropertyCoverage = (schema, keyVar, ignoreOwnUnevaluated) => {
|
|
17
|
-
if ("additionalProperties" in schema)
|
|
18
|
-
return { all: true, terms: [] };
|
|
19
|
-
if (!ignoreOwnUnevaluated && "unevaluatedProperties" in schema && schema["unevaluatedProperties"] !== false) {
|
|
20
|
-
return { all: true, terms: [] };
|
|
21
|
-
}
|
|
22
|
-
const terms = [];
|
|
23
|
-
const properties = schema["properties"];
|
|
24
|
-
if (typeof properties === "object" && properties !== null && !Array.isArray(properties)) {
|
|
25
|
-
const keys = Object.keys(properties);
|
|
26
|
-
if (keys.length > 0)
|
|
27
|
-
terms.push(`${JSON.stringify(keys)}.includes(${keyVar})`);
|
|
28
|
-
}
|
|
29
|
-
const patterns = schema["patternProperties"];
|
|
30
|
-
if (typeof patterns === "object" && patterns !== null && !Array.isArray(patterns)) {
|
|
31
|
-
for (const pattern of Object.keys(patterns)) {
|
|
32
|
-
terms.push(`${regexLiteral(pattern)}.test(${keyVar})`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return { all: false, terms };
|
|
36
|
-
};
|
|
37
|
-
const ownItemCoverage = (schema, indexVar, ignoreOwnUnevaluated) => {
|
|
38
|
-
const tupleItems = Array.isArray(schema["items"]);
|
|
39
|
-
if (!tupleItems && "items" in schema)
|
|
40
|
-
return { all: true, terms: [] };
|
|
41
|
-
if (tupleItems && "additionalItems" in schema)
|
|
42
|
-
return { all: true, terms: [] };
|
|
43
|
-
if (!ignoreOwnUnevaluated && "unevaluatedItems" in schema && schema["unevaluatedItems"] !== false) {
|
|
44
|
-
return { all: true, terms: [] };
|
|
45
|
-
}
|
|
46
|
-
const terms = [];
|
|
47
|
-
const prefix = Array.isArray(schema["prefixItems"]) ? schema["prefixItems"] : tupleItems ? schema["items"] : null;
|
|
48
|
-
if (prefix && prefix.length > 0)
|
|
49
|
-
terms.push(`${indexVar} < ${prefix.length}`);
|
|
50
|
-
return { all: false, terms };
|
|
51
|
-
};
|
|
52
|
-
const containsCoverage = (schema, indexVar, acc, ctx, match) => {
|
|
53
|
-
if (!("contains" in schema))
|
|
54
|
-
return NONE;
|
|
55
|
-
const itemMatch = match(`(${acc} as any[])[${indexVar}]`, schema["contains"], nest(ctx));
|
|
56
|
-
if (itemMatch === null)
|
|
57
|
-
return null;
|
|
58
|
-
if (itemMatch === "true")
|
|
59
|
-
return { all: true, terms: [] };
|
|
60
|
-
return { all: false, terms: [itemMatch] };
|
|
61
|
-
};
|
|
62
|
-
const coverageOf = (kind, schema, varName, acc, ctx, match, ignoreOwnUnevaluated = false) => {
|
|
63
|
-
if (typeof schema === "boolean")
|
|
64
|
-
return NONE;
|
|
65
|
-
if (!isSchemaObject(schema))
|
|
66
|
-
return null;
|
|
67
|
-
if (depthOf(ctx) > 8)
|
|
68
|
-
return null;
|
|
69
|
-
const s = schema;
|
|
70
|
-
let coverage = kind === "properties" ? ownPropertyCoverage(s, varName, ignoreOwnUnevaluated) : ownItemCoverage(s, varName, ignoreOwnUnevaluated);
|
|
71
|
-
if (kind === "items") {
|
|
72
|
-
const contains = containsCoverage(s, varName, acc, ctx, match);
|
|
73
|
-
if (contains === null)
|
|
74
|
-
return null;
|
|
75
|
-
coverage = merge(coverage, contains);
|
|
76
|
-
}
|
|
77
|
-
const ref = s["$ref"];
|
|
78
|
-
if (typeof ref === "string") {
|
|
79
|
-
if (ctx.rootSchema === void 0 || ctx.seen?.has(ref))
|
|
80
|
-
return null;
|
|
81
|
-
const target = resolveRef(ref, ctx.rootSchema);
|
|
82
|
-
if (target === void 0)
|
|
83
|
-
return null;
|
|
84
|
-
const seen = new Set(ctx.seen ?? []);
|
|
85
|
-
seen.add(ref);
|
|
86
|
-
const refCoverage = coverageOf(kind, target, varName, acc, { ...nest(ctx), seen }, match);
|
|
87
|
-
if (refCoverage === null)
|
|
88
|
-
return null;
|
|
89
|
-
coverage = merge(coverage, refCoverage);
|
|
90
|
-
}
|
|
91
|
-
if (Array.isArray(s["allOf"])) {
|
|
92
|
-
for (const member of s["allOf"]) {
|
|
93
|
-
const memberCoverage = coverageOf(kind, member, varName, acc, nest(ctx), match);
|
|
94
|
-
if (memberCoverage === null)
|
|
95
|
-
return null;
|
|
96
|
-
coverage = merge(coverage, memberCoverage);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
for (const keyword of ["anyOf", "oneOf"]) {
|
|
100
|
-
if (!Array.isArray(s[keyword]))
|
|
101
|
-
continue;
|
|
102
|
-
for (const branch of s[keyword]) {
|
|
103
|
-
const branchCoverage = coverageOf(kind, branch, varName, acc, nest(ctx), match);
|
|
104
|
-
if (branchCoverage === null)
|
|
105
|
-
return null;
|
|
106
|
-
if (branchCoverage.all === false && branchCoverage.terms.length === 0)
|
|
107
|
-
continue;
|
|
108
|
-
const branchMatch = match(acc, branch, nest(ctx));
|
|
109
|
-
if (branchMatch === null)
|
|
110
|
-
return null;
|
|
111
|
-
coverage = merge(coverage, conditioned(branchCoverage, branchMatch));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if ("if" in s) {
|
|
115
|
-
const ifMatch = match(acc, s["if"], nest(ctx));
|
|
116
|
-
if (ifMatch === null)
|
|
117
|
-
return null;
|
|
118
|
-
for (const [keyword, cond] of [
|
|
119
|
-
["if", ifMatch],
|
|
120
|
-
["then", ifMatch],
|
|
121
|
-
["else", `!(${ifMatch})`]
|
|
122
|
-
]) {
|
|
123
|
-
if (!(keyword in s))
|
|
124
|
-
continue;
|
|
125
|
-
const branchCoverage = coverageOf(kind, s[keyword], varName, acc, nest(ctx), match);
|
|
126
|
-
if (branchCoverage === null)
|
|
127
|
-
return null;
|
|
128
|
-
coverage = merge(coverage, conditioned(branchCoverage, cond));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
const dependentSchemas = s["dependentSchemas"];
|
|
132
|
-
if (typeof dependentSchemas === "object" && dependentSchemas !== null && !Array.isArray(dependentSchemas)) {
|
|
133
|
-
for (const [trigger, sub] of Object.entries(dependentSchemas)) {
|
|
134
|
-
const subCoverage = coverageOf(kind, sub, varName, acc, nest(ctx), match);
|
|
135
|
-
if (subCoverage === null)
|
|
136
|
-
return null;
|
|
137
|
-
coverage = merge(coverage, conditioned(subCoverage, `${JSON.stringify(trigger)} in (${acc} as object)`));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return coverage;
|
|
141
|
-
};
|
|
142
|
-
const unevaluatedPropertiesExpr = (acc, schema, ctx, match) => {
|
|
143
|
-
if (!isSchemaObject(schema))
|
|
144
|
-
return void 0;
|
|
145
|
-
const s = schema;
|
|
146
|
-
if (!("unevaluatedProperties" in s))
|
|
147
|
-
return void 0;
|
|
148
|
-
const unevaluated = s["unevaluatedProperties"];
|
|
149
|
-
if (unevaluated === true)
|
|
150
|
-
return void 0;
|
|
151
|
-
const d = depthOf(ctx);
|
|
152
|
-
const keyVar = `_uk${d}`;
|
|
153
|
-
const coverage = coverageOf("properties", schema, keyVar, acc, ctx, match, true);
|
|
154
|
-
if (coverage === null)
|
|
155
|
-
return null;
|
|
156
|
-
if (coverage.all)
|
|
157
|
-
return void 0;
|
|
158
|
-
const rec = `(${acc} as Record<string, unknown>)`;
|
|
159
|
-
const covered = coverage.terms.length > 0 ? orJoin(coverage.terms) : null;
|
|
160
|
-
if (unevaluated === false) {
|
|
161
|
-
if (covered === null)
|
|
162
|
-
return `Object.keys(${rec}).length === 0`;
|
|
163
|
-
return `Object.keys(${rec}).every((${keyVar}) => ${covered})`;
|
|
164
|
-
}
|
|
165
|
-
const valueMatch = match(`${rec}[${keyVar}]`, unevaluated, nest(ctx));
|
|
166
|
-
if (valueMatch === null)
|
|
167
|
-
return null;
|
|
168
|
-
if (valueMatch === "true")
|
|
169
|
-
return void 0;
|
|
170
|
-
return covered === null ? `Object.keys(${rec}).every((${keyVar}) => ${valueMatch})` : `Object.keys(${rec}).every((${keyVar}) => ${covered} || ${valueMatch})`;
|
|
171
|
-
};
|
|
172
|
-
const unevaluatedItemsExpr = (acc, schema, ctx, match) => {
|
|
173
|
-
if (!isSchemaObject(schema))
|
|
174
|
-
return void 0;
|
|
175
|
-
const s = schema;
|
|
176
|
-
if (!("unevaluatedItems" in s))
|
|
177
|
-
return void 0;
|
|
178
|
-
const unevaluated = s["unevaluatedItems"];
|
|
179
|
-
if (unevaluated === true)
|
|
180
|
-
return void 0;
|
|
181
|
-
const d = depthOf(ctx);
|
|
182
|
-
const indexVar = `_un${d}`;
|
|
183
|
-
const itemVar = `_ue${d}`;
|
|
184
|
-
const coverage = coverageOf("items", schema, indexVar, acc, ctx, match, true);
|
|
185
|
-
if (coverage === null)
|
|
186
|
-
return null;
|
|
187
|
-
if (coverage.all)
|
|
188
|
-
return void 0;
|
|
189
|
-
const elements = `(${acc} as any[])`;
|
|
190
|
-
const covered = coverage.terms.length > 0 ? orJoin(coverage.terms) : null;
|
|
191
|
-
if (unevaluated === false) {
|
|
192
|
-
if (covered === null)
|
|
193
|
-
return `${acc}.length === 0`;
|
|
194
|
-
return `${elements}.every((_, ${indexVar}) => ${covered})`;
|
|
195
|
-
}
|
|
196
|
-
const valueMatch = match(itemVar, unevaluated, nest(ctx));
|
|
197
|
-
if (valueMatch === null)
|
|
198
|
-
return null;
|
|
199
|
-
if (valueMatch === "true")
|
|
200
|
-
return void 0;
|
|
201
|
-
return covered === null ? `${elements}.every((${itemVar}) => ${valueMatch})` : `${elements}.every((${itemVar}, ${indexVar}) => ${covered} || ${valueMatch})`;
|
|
202
|
-
};
|
|
203
|
-
export {
|
|
204
|
-
unevaluatedItemsExpr,
|
|
205
|
-
unevaluatedPropertiesExpr
|
|
206
|
-
};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
2
|
-
import type { SchemaExtensions } from '../types/schema-extensions.js';
|
|
3
|
-
/**
|
|
4
|
-
* Merges custom extension properties into a schema's properties based on the
|
|
5
|
-
* definition name. Extension properties are added as optional (not included
|
|
6
|
-
* in the required array) since they are custom additions that may not always
|
|
7
|
-
* be present.
|
|
8
|
-
*
|
|
9
|
-
* If the schema is not an object, has no matching extensions, or the
|
|
10
|
-
* extensions record is empty, the original schema is returned unchanged.
|
|
11
|
-
*
|
|
12
|
-
* @param schema - The JSON Schema to extend
|
|
13
|
-
* @param definitionName - The name of the definition (e.g., "parameter")
|
|
14
|
-
* @param extensions - The extensions configuration mapping definition names to properties
|
|
15
|
-
* @returns A new schema with extension properties merged in, or the original if unchanged
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* const schema = {
|
|
20
|
-
* type: 'object',
|
|
21
|
-
* properties: { name: { type: 'string' } },
|
|
22
|
-
* required: ['name'],
|
|
23
|
-
* }
|
|
24
|
-
*
|
|
25
|
-
* const extensions = {
|
|
26
|
-
* parameter: {
|
|
27
|
-
* 'x-enabled': { type: 'boolean' },
|
|
28
|
-
* },
|
|
29
|
-
* }
|
|
30
|
-
*
|
|
31
|
-
* const extended = applySchemaExtensions(schema, 'parameter', extensions)
|
|
32
|
-
* // extended.properties now includes 'x-enabled' alongside 'name'
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export declare const applySchemaExtensions: (schema: JSONSchema, definitionName: string, extensions: SchemaExtensions) => JSONSchema;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const applySchemaExtensions = (schema, definitionName, extensions) => {
|
|
2
|
-
const extensionProperties = extensions[definitionName];
|
|
3
|
-
if (!extensionProperties || Object.keys(extensionProperties).length === 0) {
|
|
4
|
-
return schema;
|
|
5
|
-
}
|
|
6
|
-
if (typeof schema !== "object" || schema === null) {
|
|
7
|
-
return schema;
|
|
8
|
-
}
|
|
9
|
-
const existingProperties = "properties" in schema && typeof schema.properties === "object" && schema.properties !== null ? schema.properties : {};
|
|
10
|
-
return {
|
|
11
|
-
...schema,
|
|
12
|
-
properties: {
|
|
13
|
-
...existingProperties,
|
|
14
|
-
...extensionProperties
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
export {
|
|
19
|
-
applySchemaExtensions
|
|
20
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/** Names (without `.ts`) of runtime helpers that can be embedded into generated output. */
|
|
2
|
-
export type RuntimeHelperName = 'is-object' | 'validate-array' | 'validate-record' | 'has-ref';
|
|
3
|
-
/** Controls how generated parsers reference their runtime helpers. */
|
|
4
|
-
export type HelpersMode = 'package' | 'embedded';
|
|
5
|
-
export type CollectedHelpers = {
|
|
6
|
-
/** Import lines to splice into the generated file. */
|
|
7
|
-
readonly imports: string[];
|
|
8
|
-
/**
|
|
9
|
-
* Helper names that need to ship alongside the generated file in embedded mode.
|
|
10
|
-
* Includes transitive deps (e.g. `validate-record` pulls in `is-object`) so the
|
|
11
|
-
* orchestrator can emit a complete `_helpers/` directory.
|
|
12
|
-
*/
|
|
13
|
-
readonly used: Set<RuntimeHelperName>;
|
|
14
|
-
};
|
|
15
|
-
export declare const collectHelpers: (parserFunction: string, mode: HelpersMode, helpersImportPrefix?: string, importExt?: 'js' | 'ts') => CollectedHelpers;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
const PACKAGE_IMPORTS = {
|
|
2
|
-
"is-object": "import { isObject } from '@amritk/helpers/is-object';",
|
|
3
|
-
"validate-array": "import { validateArray } from '@amritk/helpers/validate-array';",
|
|
4
|
-
"validate-record": "import { validateRecord } from '@amritk/helpers/validate-record';",
|
|
5
|
-
// hasRef has historically lived in the schema-guards subpath; preserve that for package mode.
|
|
6
|
-
"has-ref": "import { hasRef } from '@amritk/helpers/schema-guards';"
|
|
7
|
-
};
|
|
8
|
-
const EMBEDDED_NAMED_EXPORTS = {
|
|
9
|
-
"is-object": "isObject",
|
|
10
|
-
"validate-array": "validateArray",
|
|
11
|
-
"validate-record": "validateRecord",
|
|
12
|
-
"has-ref": "hasRef"
|
|
13
|
-
};
|
|
14
|
-
const embeddedImport = (helper, prefix, ext) => (
|
|
15
|
-
// An explicit extension so the embedded-helper import resolves under Node ESM, not only Bun.
|
|
16
|
-
`import { ${EMBEDDED_NAMED_EXPORTS[helper]} } from '${prefix}_helpers/${helper}.${ext}';`
|
|
17
|
-
);
|
|
18
|
-
const HELPER_USAGE = /validateArray|validateRecord|isObject|hasRef\(/g;
|
|
19
|
-
const collectHelpers = (parserFunction, mode, helpersImportPrefix = "./", importExt = "js") => {
|
|
20
|
-
const imports = [];
|
|
21
|
-
const used = /* @__PURE__ */ new Set();
|
|
22
|
-
const importFor = (helper) => mode === "embedded" ? embeddedImport(helper, helpersImportPrefix, importExt) : PACKAGE_IMPORTS[helper];
|
|
23
|
-
let sawValidateArray = false;
|
|
24
|
-
let sawValidateRecord = false;
|
|
25
|
-
let sawIsObject = false;
|
|
26
|
-
let sawHasRef = false;
|
|
27
|
-
HELPER_USAGE.lastIndex = 0;
|
|
28
|
-
let match = HELPER_USAGE.exec(parserFunction);
|
|
29
|
-
while (match !== null) {
|
|
30
|
-
const token = match[0];
|
|
31
|
-
if (token === "validateArray")
|
|
32
|
-
sawValidateArray = true;
|
|
33
|
-
else if (token === "validateRecord")
|
|
34
|
-
sawValidateRecord = true;
|
|
35
|
-
else if (token === "isObject")
|
|
36
|
-
sawIsObject = true;
|
|
37
|
-
else
|
|
38
|
-
sawHasRef = true;
|
|
39
|
-
if (sawValidateArray && sawValidateRecord && sawIsObject && sawHasRef)
|
|
40
|
-
break;
|
|
41
|
-
match = HELPER_USAGE.exec(parserFunction);
|
|
42
|
-
}
|
|
43
|
-
if (sawValidateArray) {
|
|
44
|
-
imports.push(importFor("validate-array"));
|
|
45
|
-
used.add("validate-array");
|
|
46
|
-
}
|
|
47
|
-
if (sawValidateRecord) {
|
|
48
|
-
imports.push(importFor("validate-record"));
|
|
49
|
-
used.add("validate-record");
|
|
50
|
-
used.add("is-object");
|
|
51
|
-
}
|
|
52
|
-
if (sawIsObject) {
|
|
53
|
-
imports.push(importFor("is-object"));
|
|
54
|
-
used.add("is-object");
|
|
55
|
-
}
|
|
56
|
-
if (sawHasRef) {
|
|
57
|
-
imports.push(importFor("has-ref"));
|
|
58
|
-
used.add("has-ref");
|
|
59
|
-
}
|
|
60
|
-
return { imports, used };
|
|
61
|
-
};
|
|
62
|
-
export {
|
|
63
|
-
collectHelpers
|
|
64
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
2
|
-
/** Extension emitted on every relative import specifier in generated code. */
|
|
3
|
-
export type ImportExtension = 'js' | 'ts';
|
|
4
|
-
/**
|
|
5
|
-
* Options for controlling how imports are collected.
|
|
6
|
-
*/
|
|
7
|
-
type CollectImportsOptions = {
|
|
8
|
-
/**
|
|
9
|
-
* When true, only generate type-only imports (no parser function imports).
|
|
10
|
-
* Use this when generating types-only files that do not include parser functions.
|
|
11
|
-
*/
|
|
12
|
-
readonly typesOnly?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* The $ref path of the schema being generated (e.g. `#/$defs/encoding`).
|
|
15
|
-
* When provided, any $ref that resolves to the same filename is excluded from
|
|
16
|
-
* the import list, preventing a file from importing itself.
|
|
17
|
-
*/
|
|
18
|
-
readonly selfRef?: string | undefined;
|
|
19
|
-
/**
|
|
20
|
-
* The filename this file is being written as, without extension. Takes
|
|
21
|
-
* precedence over `selfRef` for self-import detection, and covers the case
|
|
22
|
-
* `selfRef` cannot: the root document has no ref of its own, so a definition
|
|
23
|
-
* whose name collides with the root type name (root `Contact` + `$defs.contact`)
|
|
24
|
-
* used to make the root file import its own type from itself.
|
|
25
|
-
*/
|
|
26
|
-
readonly selfFilename?: string | undefined;
|
|
27
|
-
/**
|
|
28
|
-
* The root schema document. When provided, URI refs that cannot be resolved
|
|
29
|
-
* within the root schema's $defs are excluded from the import list, preventing
|
|
30
|
-
* imports for external schemas that were never generated as files.
|
|
31
|
-
*/
|
|
32
|
-
readonly rootSchema?: Record<string, unknown> | undefined;
|
|
33
|
-
/**
|
|
34
|
-
* Suffix appended to every type name derived from a `$ref`. Must match the
|
|
35
|
-
* suffix used when generating the referenced files so imports resolve.
|
|
36
|
-
* Defaults to `''` (no suffix).
|
|
37
|
-
*/
|
|
38
|
-
readonly typeSuffix?: string;
|
|
39
|
-
/**
|
|
40
|
-
* Extension used on every relative import specifier. Defaults to `'js'`
|
|
41
|
-
* (the TS NodeNext form); `'ts'` makes the output runnable under Node's
|
|
42
|
-
* type stripping.
|
|
43
|
-
*/
|
|
44
|
-
readonly importExt?: ImportExtension;
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* Collects all import statements needed for a schema by finding $ref references
|
|
48
|
-
* that are directly used in the type definition (from properties, items, etc.).
|
|
49
|
-
* Does not include refs from nested schema definitions.
|
|
50
|
-
*
|
|
51
|
-
* Generates both type imports and parser function imports for each $ref.
|
|
52
|
-
* The parser imports allow validators to delegate validation to the referenced
|
|
53
|
-
* schema's parser instead of inlining the validation logic.
|
|
54
|
-
*
|
|
55
|
-
* Also detects when helper functions (isObject) and validators (validateRecord,
|
|
56
|
-
* validateArray) are needed based on the schema structure.
|
|
57
|
-
*
|
|
58
|
-
* @param schema - The JSON Schema to collect imports from
|
|
59
|
-
* @param options - Optional settings to control import generation
|
|
60
|
-
* @returns An array of import statements
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```ts
|
|
64
|
-
* const schema = {
|
|
65
|
-
* type: 'object',
|
|
66
|
-
* properties: {
|
|
67
|
-
* contact: { $ref: '#/$defs/contact' },
|
|
68
|
-
* server: { $ref: '#/$defs/server' }
|
|
69
|
-
* }
|
|
70
|
-
* }
|
|
71
|
-
* const imports = collectImports(schema)
|
|
72
|
-
* // imports = [
|
|
73
|
-
* // "import { type Contact, parseContact } from './contact';",
|
|
74
|
-
* // "import { type Server, parseServer } from './server';"
|
|
75
|
-
* // ]
|
|
76
|
-
*
|
|
77
|
-
* // With typesOnly, only type imports are generated:
|
|
78
|
-
* const typeImports = collectImports(schema, { typesOnly: true })
|
|
79
|
-
* // typeImports = [
|
|
80
|
-
* // "import type { Contact } from './contact';",
|
|
81
|
-
* // "import type { Server } from './server';"
|
|
82
|
-
* // ]
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
export declare const collectImports: (schema: JSONSchema, options?: CollectImportsOptions) => string[];
|
|
86
|
-
/**
|
|
87
|
-
* The type names a generated file will import for its `$ref`s — the same
|
|
88
|
-
* dedup/skip rules as {@link collectImports}. Generators seed their private
|
|
89
|
-
* sub-type naming with this set so a synthesized name (e.g. a root array's
|
|
90
|
-
* `FooItem`) can never shadow an imported identifier.
|
|
91
|
-
*/
|
|
92
|
-
export declare const collectImportTypeNames: (schema: JSONSchema, options?: CollectImportsOptions) => Set<string>;
|
|
93
|
-
export {};
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { refToFilename } from "@amritk/helpers/ref-to-filename";
|
|
2
|
-
import { refToName } from "@amritk/helpers/ref-to-name";
|
|
3
|
-
import { resolveRef } from "@amritk/helpers/resolve-ref";
|
|
4
|
-
import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasItems, hasOneOf, hasRef } from "@amritk/helpers/schema-guards";
|
|
5
|
-
const getImportPathForFilename = (filename, ext) => `./${filename}.${ext}`;
|
|
6
|
-
const collectImports = (schema, options) => {
|
|
7
|
-
const typesOnly = options?.typesOnly === true;
|
|
8
|
-
const importExt = options?.importExt ?? "js";
|
|
9
|
-
const importMap = /* @__PURE__ */ new Map();
|
|
10
|
-
for (const [filename, { typeName, typeOnly }] of collectImportTargets(schema, options)) {
|
|
11
|
-
const importPath = getImportPathForFilename(filename, importExt);
|
|
12
|
-
const importStatement = typesOnly || typeOnly ? `import type { ${typeName} } from '${importPath}';` : `import { type ${typeName}, parse${typeName}, validate${typeName}Shape } from '${importPath}';`;
|
|
13
|
-
importMap.set(filename, importStatement);
|
|
14
|
-
}
|
|
15
|
-
return Array.from(importMap.values()).sort();
|
|
16
|
-
};
|
|
17
|
-
const collectImportTypeNames = (schema, options) => {
|
|
18
|
-
const names = /* @__PURE__ */ new Set();
|
|
19
|
-
for (const [, target] of collectImportTargets(schema, options))
|
|
20
|
-
names.add(target.typeName);
|
|
21
|
-
return names;
|
|
22
|
-
};
|
|
23
|
-
const collectImportTargets = (schema, options) => {
|
|
24
|
-
const selfFilename = options?.selfFilename ?? (options?.selfRef ? refToFilename(options.selfRef) : void 0);
|
|
25
|
-
const rootSchema = options?.rootSchema;
|
|
26
|
-
const typeSuffix = options?.typeSuffix;
|
|
27
|
-
const refs = /* @__PURE__ */ new Set();
|
|
28
|
-
const valueRefs = /* @__PURE__ */ new Set();
|
|
29
|
-
let typeOnlyDepth = 0;
|
|
30
|
-
const collectRefsFromValue = (value) => {
|
|
31
|
-
if (typeof value !== "object" || value === null) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (Array.isArray(value)) {
|
|
35
|
-
for (const item of value) {
|
|
36
|
-
collectRefsFromValue(item);
|
|
37
|
-
}
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const record = value;
|
|
41
|
-
if (hasRef(record)) {
|
|
42
|
-
const ref = record.$ref;
|
|
43
|
-
const isInternal = ref.startsWith("#");
|
|
44
|
-
const isUri = ref.startsWith("http://") || ref.startsWith("https://");
|
|
45
|
-
const isPropertyFragment = isUri && ref.includes("#/properties/");
|
|
46
|
-
if (isInternal || isUri && !isPropertyFragment) {
|
|
47
|
-
refs.add(ref);
|
|
48
|
-
if (typeOnlyDepth === 0)
|
|
49
|
-
valueRefs.add(ref);
|
|
50
|
-
}
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
for (const keyword of ["prefixItems", "items"]) {
|
|
54
|
-
const positions = Object.hasOwn(record, keyword) ? record[keyword] : void 0;
|
|
55
|
-
if (!Array.isArray(positions))
|
|
56
|
-
continue;
|
|
57
|
-
typeOnlyDepth++;
|
|
58
|
-
for (const item of positions) {
|
|
59
|
-
collectRefsFromValue(item);
|
|
60
|
-
}
|
|
61
|
-
if (Object.hasOwn(record, "additionalItems"))
|
|
62
|
-
collectRefsFromValue(record["additionalItems"]);
|
|
63
|
-
typeOnlyDepth--;
|
|
64
|
-
}
|
|
65
|
-
if (hasAdditionalProperties(record) && hasRef(record.additionalProperties)) {
|
|
66
|
-
collectRefsFromValue(record.additionalProperties);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (record.type === "array" && hasItems(record) && !Array.isArray(record.items) && hasRef(record.items)) {
|
|
70
|
-
collectRefsFromValue(record.items);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
if (Object.hasOwn(record, "properties") && typeof record.properties === "object" && record.properties !== null) {
|
|
74
|
-
for (const value2 of Object.values(record.properties)) {
|
|
75
|
-
collectRefsFromValue(value2);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (hasOneOf(record)) {
|
|
79
|
-
for (const item of record.oneOf) {
|
|
80
|
-
collectRefsFromValue(item);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (hasAnyOf(record)) {
|
|
84
|
-
for (const item of record.anyOf) {
|
|
85
|
-
collectRefsFromValue(item);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (hasAllOf(record)) {
|
|
89
|
-
for (const item of record.allOf) {
|
|
90
|
-
collectRefsFromValue(item);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (hasItems(record) && !Array.isArray(record.items)) {
|
|
94
|
-
collectRefsFromValue(record.items);
|
|
95
|
-
}
|
|
96
|
-
if (hasAdditionalProperties(record)) {
|
|
97
|
-
collectRefsFromValue(record.additionalProperties);
|
|
98
|
-
}
|
|
99
|
-
if (Object.hasOwn(record, "patternProperties") && typeof record.patternProperties === "object" && record.patternProperties !== null) {
|
|
100
|
-
for (const value2 of Object.values(record.patternProperties)) {
|
|
101
|
-
collectRefsFromValue(value2);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (Object.hasOwn(record, "if")) {
|
|
105
|
-
collectRefsFromValue(record.if);
|
|
106
|
-
}
|
|
107
|
-
if (Object.hasOwn(record, "then")) {
|
|
108
|
-
collectRefsFromValue(record.then);
|
|
109
|
-
}
|
|
110
|
-
if (Object.hasOwn(record, "else")) {
|
|
111
|
-
collectRefsFromValue(record.else);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
if (typeof schema === "object" && schema !== null && hasRef(schema)) {
|
|
115
|
-
const ref = schema.$ref;
|
|
116
|
-
const isInternal = ref.startsWith("#");
|
|
117
|
-
const isUri = ref.startsWith("http://") || ref.startsWith("https://");
|
|
118
|
-
const isPropertyFragment = isUri && ref.includes("#/properties/");
|
|
119
|
-
if (isInternal || isUri && !isPropertyFragment) {
|
|
120
|
-
refs.add(ref);
|
|
121
|
-
valueRefs.add(ref);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "properties") && typeof schema.properties === "object" && schema.properties !== null) {
|
|
125
|
-
for (const value of Object.values(schema.properties)) {
|
|
126
|
-
collectRefsFromValue(value);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "additionalProperties")) {
|
|
130
|
-
collectRefsFromValue(schema.additionalProperties);
|
|
131
|
-
}
|
|
132
|
-
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "patternProperties") && typeof schema.patternProperties === "object" && schema.patternProperties !== null) {
|
|
133
|
-
for (const value of Object.values(schema.patternProperties)) {
|
|
134
|
-
collectRefsFromValue(value);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (typeof schema === "object" && schema !== null && hasItems(schema) && !Array.isArray(schema.items)) {
|
|
138
|
-
collectRefsFromValue(schema.items);
|
|
139
|
-
}
|
|
140
|
-
if (typeof schema === "object" && schema !== null) {
|
|
141
|
-
const record = schema;
|
|
142
|
-
for (const keyword of ["prefixItems", "items"]) {
|
|
143
|
-
const positions = Object.hasOwn(record, keyword) ? record[keyword] : void 0;
|
|
144
|
-
if (!Array.isArray(positions))
|
|
145
|
-
continue;
|
|
146
|
-
typeOnlyDepth++;
|
|
147
|
-
for (const item of positions)
|
|
148
|
-
collectRefsFromValue(item);
|
|
149
|
-
if (Object.hasOwn(record, "additionalItems"))
|
|
150
|
-
collectRefsFromValue(record["additionalItems"]);
|
|
151
|
-
typeOnlyDepth--;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (typeof schema === "object" && schema !== null && hasOneOf(schema)) {
|
|
155
|
-
for (const item of schema.oneOf) {
|
|
156
|
-
collectRefsFromValue(item);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (typeof schema === "object" && schema !== null && hasAnyOf(schema)) {
|
|
160
|
-
for (const item of schema.anyOf) {
|
|
161
|
-
collectRefsFromValue(item);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (typeof schema === "object" && schema !== null && hasAllOf(schema)) {
|
|
165
|
-
for (const item of schema.allOf) {
|
|
166
|
-
collectRefsFromValue(item);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "if")) {
|
|
170
|
-
collectRefsFromValue(schema.if);
|
|
171
|
-
}
|
|
172
|
-
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "then")) {
|
|
173
|
-
collectRefsFromValue(schema.then);
|
|
174
|
-
}
|
|
175
|
-
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "else")) {
|
|
176
|
-
collectRefsFromValue(schema.else);
|
|
177
|
-
}
|
|
178
|
-
const targets = /* @__PURE__ */ new Map();
|
|
179
|
-
for (const ref of refs) {
|
|
180
|
-
if (rootSchema !== void 0 && (ref.startsWith("http://") || ref.startsWith("https://"))) {
|
|
181
|
-
if (!resolveRef(ref, rootSchema))
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
const typeName = refToName(ref, typeSuffix);
|
|
185
|
-
const filename = refToFilename(ref);
|
|
186
|
-
if (selfFilename !== void 0 && filename === selfFilename) {
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
const typeOnly = !valueRefs.has(ref) && (targets.get(filename)?.typeOnly ?? true);
|
|
190
|
-
targets.set(filename, { typeName, typeOnly });
|
|
191
|
-
}
|
|
192
|
-
return targets;
|
|
193
|
-
};
|
|
194
|
-
export {
|
|
195
|
-
collectImportTypeNames,
|
|
196
|
-
collectImports
|
|
197
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
2
|
-
/**
|
|
3
|
-
* Attempts to find a discriminator property that can be used to distinguish between union types.
|
|
4
|
-
* Returns the property name if a suitable discriminator is found, otherwise null.
|
|
5
|
-
*/
|
|
6
|
-
export declare const findDiscriminator: (schemas: readonly JSONSchema[]) => string | null;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { hasConst, hasEnum, hasProperties, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
2
|
-
const findDiscriminator = (schemas) => {
|
|
3
|
-
const propertyValues = /* @__PURE__ */ new Map();
|
|
4
|
-
for (const schema of schemas) {
|
|
5
|
-
if (isObjectSchema(schema) && hasProperties(schema)) {
|
|
6
|
-
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
7
|
-
if (!propertyValues.has(key)) {
|
|
8
|
-
propertyValues.set(key, /* @__PURE__ */ new Set());
|
|
9
|
-
}
|
|
10
|
-
if (isSchemaObject(propSchema) && hasConst(propSchema)) {
|
|
11
|
-
propertyValues.get(key)?.add(propSchema.const);
|
|
12
|
-
} else if (isSchemaObject(propSchema) && hasEnum(propSchema) && propSchema.enum.length === 1) {
|
|
13
|
-
propertyValues.get(key)?.add(propSchema.enum[0]);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
for (const [key, values] of propertyValues.entries()) {
|
|
19
|
-
if (values.size === schemas.length) {
|
|
20
|
-
return key;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
};
|
|
25
|
-
export {
|
|
26
|
-
findDiscriminator
|
|
27
|
-
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
2
|
-
/**
|
|
3
|
-
* Returns the default value for a JSON Schema property.
|
|
4
|
-
* Priority order: explicit default > first enum value > first example > union first schema > pattern-based > type-based.
|
|
5
|
-
* These defaults ensure that parsing never fails, even with missing data.
|
|
6
|
-
*/
|
|
7
|
-
export declare const getDefaultValue: (schema: JSONSchema) => string;
|