@amritk/generate-parsers 0.7.2 → 0.9.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 +1 -1
- package/dist/generators/build-schema.d.ts +0 -1
- package/dist/generators/generate-default-from-pattern.d.ts +0 -1
- package/dist/generators/generate-discriminated-union-validation.d.ts +0 -1
- package/dist/generators/generate-enum-check.d.ts +0 -1
- package/dist/generators/generate-files.d.ts +0 -1
- package/dist/generators/generate-non-discriminated-union-validation.d.ts +0 -1
- package/dist/generators/generate-parser-function.d.ts +3 -3
- package/dist/generators/generate-parser-function.js +267 -32
- package/dist/generators/generate-schema-checks.d.ts +0 -1
- package/dist/generators/generate-strict-assertion.d.ts +0 -1
- package/dist/generators/generate-validation-expression.d.ts +0 -1
- package/dist/helpers/apply-schema-extensions.d.ts +0 -1
- package/dist/helpers/collect-helpers.d.ts +0 -1
- package/dist/helpers/collect-imports.d.ts +0 -1
- package/dist/helpers/find-discriminator.d.ts +0 -1
- package/dist/helpers/get-default-value.d.ts +0 -1
- package/dist/helpers/get-discriminator-value.d.ts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/types/schema-extensions.d.ts +0 -1
- package/package.json +3 -5
- package/dist/generators/build-schema.d.ts.map +0 -1
- package/dist/generators/generate-default-from-pattern.d.ts.map +0 -1
- package/dist/generators/generate-discriminated-union-validation.d.ts.map +0 -1
- package/dist/generators/generate-enum-check.d.ts.map +0 -1
- package/dist/generators/generate-files.d.ts.map +0 -1
- package/dist/generators/generate-non-discriminated-union-validation.d.ts.map +0 -1
- package/dist/generators/generate-parser-function.d.ts.map +0 -1
- package/dist/generators/generate-schema-checks.d.ts.map +0 -1
- package/dist/generators/generate-strict-assertion.d.ts.map +0 -1
- package/dist/generators/generate-validation-expression.d.ts.map +0 -1
- package/dist/helpers/apply-schema-extensions.d.ts.map +0 -1
- package/dist/helpers/collect-helpers.d.ts.map +0 -1
- package/dist/helpers/collect-imports.d.ts.map +0 -1
- package/dist/helpers/find-discriminator.d.ts.map +0 -1
- package/dist/helpers/get-default-value.d.ts.map +0 -1
- package/dist/helpers/get-discriminator-value.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/types/schema-extensions.d.ts.map +0 -1
- package/src/generators/build-schema.test.ts +0 -854
- package/src/generators/build-schema.ts +0 -147
- package/src/generators/generate-default-from-pattern.test.ts +0 -81
- package/src/generators/generate-default-from-pattern.ts +0 -39
- package/src/generators/generate-discriminated-union-validation.test.ts +0 -84
- package/src/generators/generate-discriminated-union-validation.ts +0 -54
- package/src/generators/generate-enum-check.test.ts +0 -45
- package/src/generators/generate-enum-check.ts +0 -8
- package/src/generators/generate-files.test.ts +0 -448
- package/src/generators/generate-files.ts +0 -155
- package/src/generators/generate-non-discriminated-union-validation.test.ts +0 -74
- package/src/generators/generate-non-discriminated-union-validation.ts +0 -40
- package/src/generators/generate-parser-function.test.ts +0 -1794
- package/src/generators/generate-parser-function.ts +0 -1340
- package/src/generators/generate-schema-checks.test.ts +0 -330
- package/src/generators/generate-schema-checks.ts +0 -128
- package/src/generators/generate-strict-assertion.test.ts +0 -67
- package/src/generators/generate-strict-assertion.ts +0 -243
- package/src/generators/generate-validation-expression.test.ts +0 -831
- package/src/generators/generate-validation-expression.ts +0 -327
- package/src/helpers/apply-schema-extensions.test.ts +0 -194
- package/src/helpers/apply-schema-extensions.ts +0 -63
- package/src/helpers/collect-helpers.test.ts +0 -115
- package/src/helpers/collect-helpers.ts +0 -79
- package/src/helpers/collect-imports.test.ts +0 -547
- package/src/helpers/collect-imports.ts +0 -275
- package/src/helpers/find-discriminator.test.ts +0 -88
- package/src/helpers/find-discriminator.ts +0 -39
- package/src/helpers/get-default-value.test.ts +0 -139
- package/src/helpers/get-default-value.ts +0 -95
- package/src/helpers/get-discriminator-value.test.ts +0 -83
- package/src/helpers/get-discriminator-value.ts +0 -21
- package/src/index.ts +0 -1
- package/src/types/schema-extensions.ts +0 -35
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import { escapeRegexPattern } from '@amritk/helpers/escape-regex-pattern'
|
|
2
|
-
import { getMjstInstanceOf, getMjstPrimitive } from '@amritk/helpers/mjst-extension'
|
|
3
|
-
import { safeAccessor } from '@amritk/helpers/safe-accessor'
|
|
4
|
-
import {
|
|
5
|
-
hasEnum,
|
|
6
|
-
hasExclusiveMaximum,
|
|
7
|
-
hasExclusiveMinimum,
|
|
8
|
-
hasMaximum,
|
|
9
|
-
hasMaxLength,
|
|
10
|
-
hasMinimum,
|
|
11
|
-
hasMinLength,
|
|
12
|
-
hasMultipleOf,
|
|
13
|
-
hasPattern,
|
|
14
|
-
hasProperties,
|
|
15
|
-
hasRef,
|
|
16
|
-
hasRequired,
|
|
17
|
-
hasType,
|
|
18
|
-
isSchemaObject,
|
|
19
|
-
} from '@amritk/helpers/schema-guards'
|
|
20
|
-
import type { JSONSchema } from 'json-schema-typed/draft-2020-12'
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Returns the inline condition that is true when `accessor` is the wrong type
|
|
24
|
-
* for the given JSON Schema primitive type.
|
|
25
|
-
*/
|
|
26
|
-
const wrongTypeCondition = (accessor: string, type: string): string | null => {
|
|
27
|
-
switch (type) {
|
|
28
|
-
case 'string':
|
|
29
|
-
return `typeof ${accessor} !== "string"`
|
|
30
|
-
case 'number':
|
|
31
|
-
case 'integer':
|
|
32
|
-
return `typeof ${accessor} !== "number"`
|
|
33
|
-
case 'boolean':
|
|
34
|
-
return `typeof ${accessor} !== "boolean"`
|
|
35
|
-
case 'array':
|
|
36
|
-
return `!Array.isArray(${accessor})`
|
|
37
|
-
case 'object':
|
|
38
|
-
return `!isObject(${accessor})`
|
|
39
|
-
default:
|
|
40
|
-
return null
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Maps a JSON Schema type to the label used in error messages.
|
|
46
|
-
* `integer` collapses to `number` since both are validated via `typeof === "number"`.
|
|
47
|
-
*/
|
|
48
|
-
const typeLabel = (type: string): string => (type === 'integer' ? 'number' : type)
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Generates strict-mode constraint checks for a typed property
|
|
52
|
-
* (pattern, length, min/max, multipleOf).
|
|
53
|
-
*/
|
|
54
|
-
const generateConstraintChecks = (acc: string, propSchema: JSONSchema, typeName: string, key: string): string[] => {
|
|
55
|
-
if (!isSchemaObject(propSchema) || !hasType(propSchema)) return []
|
|
56
|
-
const t = propSchema.type as string
|
|
57
|
-
const lines: string[] = []
|
|
58
|
-
|
|
59
|
-
if (t === 'string') {
|
|
60
|
-
if (hasPattern(propSchema)) {
|
|
61
|
-
const pattern = escapeRegexPattern(propSchema.pattern)
|
|
62
|
-
lines.push(
|
|
63
|
-
` if (typeof ${acc} === "string" && !/${pattern}/.test(${acc})) throw new Error('[${typeName}] field "${key}" must match pattern ${propSchema.pattern}');`,
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
if (hasMinLength(propSchema)) {
|
|
67
|
-
lines.push(
|
|
68
|
-
` if (typeof ${acc} === "string" && ${acc}.length < ${propSchema.minLength}) throw new Error('[${typeName}] field "${key}" must have at least ${propSchema.minLength} characters');`,
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
if (hasMaxLength(propSchema)) {
|
|
72
|
-
lines.push(
|
|
73
|
-
` if (typeof ${acc} === "string" && ${acc}.length > ${propSchema.maxLength}) throw new Error('[${typeName}] field "${key}" must have at most ${propSchema.maxLength} characters');`,
|
|
74
|
-
)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (t === 'number' || t === 'integer') {
|
|
79
|
-
if (hasMinimum(propSchema)) {
|
|
80
|
-
lines.push(
|
|
81
|
-
` if (typeof ${acc} === "number" && ${acc} < ${propSchema.minimum}) throw new Error('[${typeName}] field "${key}" must be >= ${propSchema.minimum}');`,
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
if (hasMaximum(propSchema)) {
|
|
85
|
-
lines.push(
|
|
86
|
-
` if (typeof ${acc} === "number" && ${acc} > ${propSchema.maximum}) throw new Error('[${typeName}] field "${key}" must be <= ${propSchema.maximum}');`,
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
if (hasExclusiveMinimum(propSchema)) {
|
|
90
|
-
lines.push(
|
|
91
|
-
` if (typeof ${acc} === "number" && ${acc} <= ${propSchema.exclusiveMinimum}) throw new Error('[${typeName}] field "${key}" must be > ${propSchema.exclusiveMinimum}');`,
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
if (hasExclusiveMaximum(propSchema)) {
|
|
95
|
-
lines.push(
|
|
96
|
-
` if (typeof ${acc} === "number" && ${acc} >= ${propSchema.exclusiveMaximum}) throw new Error('[${typeName}] field "${key}" must be < ${propSchema.exclusiveMaximum}');`,
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
if (hasMultipleOf(propSchema)) {
|
|
100
|
-
lines.push(
|
|
101
|
-
` if (typeof ${acc} === "number" && ${acc} % ${propSchema.multipleOf} !== 0) throw new Error('[${typeName}] field "${key}" must be a multiple of ${propSchema.multipleOf}');`,
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return lines
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Generates strict-mode lines for a single property of an object schema.
|
|
111
|
-
* Properties with a `$ref` are skipped here — the nested parser handles its own
|
|
112
|
-
* strict check when called from the parent's slow path.
|
|
113
|
-
*/
|
|
114
|
-
const generatePropertyAssertion = (
|
|
115
|
-
key: string,
|
|
116
|
-
propSchema: JSONSchema,
|
|
117
|
-
isRequired: boolean,
|
|
118
|
-
typeName: string,
|
|
119
|
-
): string[] => {
|
|
120
|
-
const acc = safeAccessor('input', key)
|
|
121
|
-
const lines: string[] = []
|
|
122
|
-
|
|
123
|
-
if (isRequired) {
|
|
124
|
-
lines.push(
|
|
125
|
-
` if (!(${JSON.stringify(key)} in input)) throw new Error('[${typeName}] missing required property "${key}"');`,
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (!isSchemaObject(propSchema)) return lines
|
|
130
|
-
if (hasRef(propSchema)) return lines
|
|
131
|
-
|
|
132
|
-
const instanceOf = getMjstInstanceOf(propSchema)
|
|
133
|
-
if (instanceOf) {
|
|
134
|
-
if (isRequired) {
|
|
135
|
-
lines.push(
|
|
136
|
-
` if (!(${acc} instanceof ${instanceOf})) throw new Error('[${typeName}] field "${key}" must be ${instanceOf}');`,
|
|
137
|
-
)
|
|
138
|
-
} else {
|
|
139
|
-
lines.push(
|
|
140
|
-
` if (${acc} !== undefined && !(${acc} instanceof ${instanceOf})) throw new Error('[${typeName}] field "${key}" must be ${instanceOf}');`,
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
return lines
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const primitive = getMjstPrimitive(propSchema)
|
|
147
|
-
if (primitive) {
|
|
148
|
-
if (isRequired) {
|
|
149
|
-
lines.push(
|
|
150
|
-
` if (typeof ${acc} !== "${primitive}") throw new Error('[${typeName}] field "${key}" must be ${primitive}');`,
|
|
151
|
-
)
|
|
152
|
-
} else {
|
|
153
|
-
lines.push(
|
|
154
|
-
` if (${acc} !== undefined && typeof ${acc} !== "${primitive}") throw new Error('[${typeName}] field "${key}" must be ${primitive}');`,
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
return lines
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (hasEnum(propSchema)) {
|
|
161
|
-
const allowed = JSON.stringify(propSchema.enum)
|
|
162
|
-
const label = (propSchema.enum as unknown[]).map((v) => JSON.stringify(v)).join(', ')
|
|
163
|
-
if (isRequired) {
|
|
164
|
-
lines.push(
|
|
165
|
-
` if (!(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`,
|
|
166
|
-
)
|
|
167
|
-
} else {
|
|
168
|
-
lines.push(
|
|
169
|
-
` if (${acc} !== undefined && !(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`,
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
return lines
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (hasType(propSchema)) {
|
|
176
|
-
const t = propSchema.type as string
|
|
177
|
-
const wrongType = wrongTypeCondition(acc, t)
|
|
178
|
-
if (wrongType) {
|
|
179
|
-
if (isRequired) {
|
|
180
|
-
lines.push(
|
|
181
|
-
` if (${wrongType}) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`,
|
|
182
|
-
)
|
|
183
|
-
} else {
|
|
184
|
-
lines.push(
|
|
185
|
-
` if (${acc} !== undefined && (${wrongType})) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`,
|
|
186
|
-
)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
lines.push(...generateConstraintChecks(acc, propSchema, typeName, key))
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return lines
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Generates strict-mode assertions for the body of an object parser.
|
|
197
|
-
* Throws on:
|
|
198
|
-
* - non-object input
|
|
199
|
-
* - missing required property
|
|
200
|
-
* - property of the wrong primitive type
|
|
201
|
-
* - enum / pattern / length / min / max / multipleOf violations
|
|
202
|
-
*
|
|
203
|
-
* Properties with a `$ref` are validated by the nested parser's own strict
|
|
204
|
-
* check when that parser is invoked downstream.
|
|
205
|
-
*/
|
|
206
|
-
export const generateObjectStrictAssertion = (schema: JSONSchema, typeName: string): string[] => {
|
|
207
|
-
const lines: string[] = []
|
|
208
|
-
lines.push(
|
|
209
|
-
` if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);`,
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
if (!hasProperties(schema) || !isSchemaObject(schema)) return lines
|
|
213
|
-
|
|
214
|
-
const required = new Set<string>(hasRequired(schema) ? schema.required : [])
|
|
215
|
-
|
|
216
|
-
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
217
|
-
lines.push(...generatePropertyAssertion(key, propSchema, required.has(key), typeName))
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return lines
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Generates a single strict-mode assertion line for a non-object scalar parser.
|
|
225
|
-
* Returns null when the schema has no type information to assert on.
|
|
226
|
-
*/
|
|
227
|
-
export const generateScalarStrictAssertion = (schema: JSONSchema, typeName: string): string | null => {
|
|
228
|
-
const instanceOf = getMjstInstanceOf(schema)
|
|
229
|
-
if (instanceOf) {
|
|
230
|
-
return ` if (!(input instanceof ${instanceOf})) throw new Error(\`[${typeName}] expected ${instanceOf}, got \${input === null ? "null" : typeof input}\`);`
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const primitive = getMjstPrimitive(schema)
|
|
234
|
-
if (primitive) {
|
|
235
|
-
return ` if (typeof input !== "${primitive}") throw new Error(\`[${typeName}] expected ${primitive}, got \${input === null ? "null" : typeof input}\`);`
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (!isSchemaObject(schema) || !hasType(schema)) return null
|
|
239
|
-
const t = schema.type as string
|
|
240
|
-
const wrongType = wrongTypeCondition('input', t)
|
|
241
|
-
if (!wrongType) return null
|
|
242
|
-
return ` if (${wrongType}) throw new Error(\`[${typeName}] expected ${typeLabel(t)}, got \${input === null ? "null" : typeof input}\`);`
|
|
243
|
-
}
|