@amritk/generate-parsers 0.17.2 → 0.19.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/AI.md +10 -0
- package/README.md +147 -19
- package/dist/generators/assert-supported-keywords.d.ts +13 -9
- package/dist/generators/assert-supported-keywords.js +58 -18
- package/dist/generators/build-schema.d.ts +9 -1
- package/dist/generators/build-schema.js +5 -2
- package/dist/generators/generate-deep-equal-check.d.ts +24 -0
- package/dist/generators/generate-deep-equal-check.js +35 -0
- package/dist/generators/generate-discriminated-union-validation.js +1 -2
- package/dist/generators/generate-enum-check.d.ts +8 -3
- package/dist/generators/generate-enum-check.js +8 -6
- package/dist/generators/generate-files.d.ts +7 -0
- package/dist/generators/generate-files.js +23 -4
- package/dist/generators/generate-parser-function.d.ts +1 -1
- package/dist/generators/generate-parser-function.js +212 -65
- package/dist/generators/generate-schema-checks.js +10 -8
- package/dist/generators/generate-strict-assertion.d.ts +89 -5
- package/dist/generators/generate-strict-assertion.js +429 -58
- package/dist/generators/generate-type-checks.d.ts +46 -10
- package/dist/generators/generate-type-checks.js +112 -16
- package/dist/generators/generate-unique-items-check.d.ts +16 -0
- package/dist/generators/generate-unique-items-check.js +32 -0
- package/dist/generators/generate-validation-expression.d.ts +29 -4
- package/dist/generators/generate-validation-expression.js +15 -6
- package/dist/generators/subschema-match.d.ts +23 -4
- package/dist/generators/subschema-match.js +312 -48
- package/dist/generators/unevaluated-match.d.ts +19 -0
- package/dist/generators/unevaluated-match.js +206 -0
- package/dist/helpers/collect-imports.d.ts +8 -0
- package/dist/helpers/collect-imports.js +1 -1
- package/dist/helpers/get-default-value.js +45 -2
- package/package.json +9 -3
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { regexLiteral } from "@amritk/helpers/escape-regex-pattern";
|
|
2
2
|
import { quoteJsString } from "@amritk/helpers/quote-js-string";
|
|
3
3
|
import { refToName } from "@amritk/helpers/ref-to-name";
|
|
4
4
|
import { resolveRef } from "@amritk/helpers/resolve-ref";
|
|
5
|
-
import { safeAccessor, safeKey } from "@amritk/helpers/safe-accessor";
|
|
6
|
-
import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasEnum, hasItems, hasOneOf, hasProperties, hasRef, hasRequired, hasType, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
5
|
+
import { missingCheck, safeAccessor, safeKey } from "@amritk/helpers/safe-accessor";
|
|
6
|
+
import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasEnum, hasItems, hasMaxItems, hasMaxProperties, hasMinItems, hasMinProperties, hasOneOf, hasProperties, hasRef, hasRequired, hasType, hasUniqueItems, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
7
7
|
import { unknownKeyCheck } from "@amritk/helpers/unknown-key-check";
|
|
8
8
|
import { findDiscriminator } from "../helpers/find-discriminator.js";
|
|
9
9
|
import { getDefaultValue } from "../helpers/get-default-value.js";
|
|
10
10
|
import { getDiscriminatorValue } from "../helpers/get-discriminator-value.js";
|
|
11
11
|
import { assertNoUnsupportedKeywords } from "./assert-supported-keywords.js";
|
|
12
12
|
import { generateEnumCaseInsensitiveCoercion } from "./generate-enum-check.js";
|
|
13
|
-
import { generateContainsCheck, generateObjectKeywordChecks, generateObjectStrictAssertion, generateScalarStrictAssertion } from "./generate-strict-assertion.js";
|
|
14
|
-
import { canEnforceUnion, generatePropertyTypeCheck, generateUnionCheck, getUnionBranches, hasFastPathBlockingKeyword, isInlineObjectArrayProperty, isInlineObjectProperty, shapeValidatorName } from "./generate-type-checks.js";
|
|
13
|
+
import { generateBackstopAssertion, generateCompositionChecks, generateContainsCheck, generateKeyedValueChecks, generateObjectKeywordChecks, generateObjectSizeChecks, generateObjectStrictAssertion, generateScalarStrictAssertion, inlineAllOfMembers } from "./generate-strict-assertion.js";
|
|
14
|
+
import { canEnforceUnion, generatePropertyTypeCheck, generateUnionCheck, getUnionBranches, hasFastPathBlockingKeyword, isExclusiveUnion, isInlineObjectArrayProperty, isInlineObjectProperty, shapeValidatorName } from "./generate-type-checks.js";
|
|
15
|
+
import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
|
|
15
16
|
import { generatePrefixItemsMap, generateValidationExpression, getPrefixItems, isCoercibleItemSchema, prefixItemsCapsLength } from "./generate-validation-expression.js";
|
|
16
17
|
const generateParserName = (typeName) => {
|
|
17
18
|
return `parse${typeName}`;
|
|
@@ -149,6 +150,8 @@ const generateFallbackValue = (_, propSchema, useRefImports, suffix) => {
|
|
|
149
150
|
}
|
|
150
151
|
return getDefaultValue(propSchema);
|
|
151
152
|
};
|
|
153
|
+
const PROTOTYPE_MEMBER_NAMES = new Set(Object.getOwnPropertyNames(Object.prototype));
|
|
154
|
+
const declaresPrototypeMemberProperty = (schema) => hasProperties(schema) && Object.keys(schema.properties).some((key) => PROTOTYPE_MEMBER_NAMES.has(key));
|
|
152
155
|
const generateFallbackObject = (schema, useRefImports, typeName, suffix, subTypeNames) => {
|
|
153
156
|
if (!hasProperties(schema)) {
|
|
154
157
|
return `{} as ${typeName}`;
|
|
@@ -165,7 +168,8 @@ const generateFallbackObject = (schema, useRefImports, typeName, suffix, subType
|
|
|
165
168
|
if (fallbackValue === "undefined") {
|
|
166
169
|
return `{} as ${typeName}`;
|
|
167
170
|
}
|
|
168
|
-
|
|
171
|
+
const partialFallback = fallbackValue === "{}" || isSchemaObject(propSchema) && (hasAllOf(propSchema) || hasOneOf(propSchema) || hasAnyOf(propSchema));
|
|
172
|
+
requiredProps.push(` ${safeLiteralKey(key)}: ${partialFallback ? `${fallbackValue} as NonNullable<${typeName}>[${JSON.stringify(key)}]` : fallbackValue},`);
|
|
169
173
|
}
|
|
170
174
|
}
|
|
171
175
|
if (requiredProps.length === 0) {
|
|
@@ -179,7 +183,7 @@ const generateFallbackObject = (schema, useRefImports, typeName, suffix, subType
|
|
|
179
183
|
}
|
|
180
184
|
}
|
|
181
185
|
result += "\n }";
|
|
182
|
-
return result;
|
|
186
|
+
return declaresPrototypeMemberProperty(schema) ? `${result} as ${typeName}` : result;
|
|
183
187
|
};
|
|
184
188
|
const scalarDefaultLiteral = (schema) => {
|
|
185
189
|
if (!isSchemaObject(schema))
|
|
@@ -207,7 +211,7 @@ const scalarDefaultLiteral = (schema) => {
|
|
|
207
211
|
}
|
|
208
212
|
return "{}";
|
|
209
213
|
};
|
|
210
|
-
const generateRefUnionDispatch = (functionName, typeName, branches, strict, ctx) => {
|
|
214
|
+
const generateRefUnionDispatch = (functionName, typeName, schema, branches, strict, ctx) => {
|
|
211
215
|
const rootSchema = ctx.rootSchema;
|
|
212
216
|
if (!ctx.useRefImports || rootSchema === void 0)
|
|
213
217
|
return null;
|
|
@@ -238,17 +242,26 @@ const generateRefUnionDispatch = (functionName, typeName, branches, strict, ctx)
|
|
|
238
242
|
if (c)
|
|
239
243
|
expr = `_disc === ${JSON.stringify(c.value)} ? ${c.parser}(input) : ${expr}`;
|
|
240
244
|
}
|
|
245
|
+
const backstop = strict ? generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(ctx)) : [];
|
|
246
|
+
const prelude = backstop.length > 0 ? `${backstop.join("\n")}
|
|
247
|
+
` : "";
|
|
241
248
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
242
|
-
const _disc = ${discExpr};
|
|
249
|
+
${prelude} const _disc = ${discExpr};
|
|
243
250
|
return ${expr};
|
|
244
251
|
};`;
|
|
245
252
|
};
|
|
253
|
+
const strictContext = (ctx) => ({
|
|
254
|
+
useRefImports: ctx?.useRefImports ?? false,
|
|
255
|
+
suffix: ctx?.suffix ?? "",
|
|
256
|
+
...ctx?.rootSchema !== void 0 ? { rootSchema: ctx.rootSchema } : {},
|
|
257
|
+
...ctx?.stripUnknown !== void 0 ? { stripUnknown: ctx.stripUnknown } : {}
|
|
258
|
+
});
|
|
246
259
|
const generateNonObjectParser = (typeName, schema, strict, unionCtx) => {
|
|
247
260
|
const functionName = generateParserName(typeName);
|
|
248
261
|
if (unionCtx && isSchemaObject(schema) && (hasOneOf(schema) || hasAnyOf(schema))) {
|
|
249
262
|
const branches = hasOneOf(schema) ? schema.oneOf : hasAnyOf(schema) ? schema.anyOf : [];
|
|
250
263
|
if (branches.length > 0 && branches.every((b) => isSchemaObject(b) && hasRef(b))) {
|
|
251
|
-
const dispatch = generateRefUnionDispatch(functionName, typeName, branches, strict, unionCtx);
|
|
264
|
+
const dispatch = generateRefUnionDispatch(functionName, typeName, schema, branches, strict, unionCtx);
|
|
252
265
|
if (dispatch !== null)
|
|
253
266
|
return dispatch;
|
|
254
267
|
}
|
|
@@ -256,16 +269,28 @@ const generateNonObjectParser = (typeName, schema, strict, unionCtx) => {
|
|
|
256
269
|
if (unionCtx?.useRefImports && isSchemaObject(schema) && hasRef(schema) && !hasProperties(schema)) {
|
|
257
270
|
const refName = refToName(schema.$ref, unionCtx.suffix);
|
|
258
271
|
if (refName !== typeName) {
|
|
259
|
-
|
|
272
|
+
const assertion = strict ? generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx)) : [];
|
|
273
|
+
if (assertion.length === 0) {
|
|
274
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => ${generateParserName(refName)}(input) as ${typeName};`;
|
|
275
|
+
}
|
|
276
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
277
|
+
${assertion.join("\n")}
|
|
278
|
+
return ${generateParserName(refName)}(input) as ${typeName};
|
|
279
|
+
};`;
|
|
260
280
|
}
|
|
261
281
|
}
|
|
262
282
|
if (strict && unionCtx && isSchemaObject(schema)) {
|
|
263
283
|
const branches = getUnionBranches(schema);
|
|
264
284
|
if (branches && !unionCtx.stripUnknown && canEnforceUnion(branches, unionCtx.rootSchema)) {
|
|
265
|
-
const check = generateUnionCheck("input", branches, unionCtx.useRefImports, unionCtx.suffix);
|
|
285
|
+
const check = generateUnionCheck("input", branches, unionCtx.useRefImports, unionCtx.suffix, isExclusiveUnion(schema));
|
|
266
286
|
if (check !== null) {
|
|
287
|
+
const backstop = generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx));
|
|
288
|
+
const assertions = [
|
|
289
|
+
` if (!(${check})) throw new Error(${quoteJsString(`[${typeName}] value does not match any union branch`)});`,
|
|
290
|
+
...backstop
|
|
291
|
+
].join("\n");
|
|
267
292
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
268
|
-
|
|
293
|
+
${assertions}
|
|
269
294
|
return input as ${typeName};
|
|
270
295
|
};`;
|
|
271
296
|
}
|
|
@@ -295,8 +320,8 @@ const generateNonObjectParser = (typeName, schema, strict, unionCtx) => {
|
|
|
295
320
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
296
321
|
}
|
|
297
322
|
}
|
|
298
|
-
if (strict && isSchemaObject(schema) && !hasType(schema) && (hasConst(schema) || hasEnum(schema))) {
|
|
299
|
-
const assertion = generateScalarStrictAssertion(schema, typeName, unionCtx
|
|
323
|
+
if (strict && isSchemaObject(schema) && !hasType(schema) && (hasConst(schema) || hasEnum(schema) || hasAllOf(schema) || hasOneOf(schema) || hasAnyOf(schema) || "not" in schema || "if" in schema)) {
|
|
324
|
+
const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
|
|
300
325
|
if (assertion !== null) {
|
|
301
326
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
302
327
|
${assertion}
|
|
@@ -307,7 +332,26 @@ ${assertion}
|
|
|
307
332
|
if (isSchemaObject(schema) && hasType(schema) && schema.type === "array" && hasItems(schema) && !Array.isArray(schema.items)) {
|
|
308
333
|
const items = schema.items;
|
|
309
334
|
const notArrayThrow = `if (!Array.isArray(input)) throw new Error(\`[${typeName}] expected array, got \${input === null ? "null" : typeof input}\`);`;
|
|
310
|
-
const strictArrayChecks = strict ?
|
|
335
|
+
const strictArrayChecks = strict ? [
|
|
336
|
+
// `minItems`/`maxItems` were dropped on this path entirely: a root
|
|
337
|
+
// array of objects or `$ref`s bypasses generateScalarStrictAssertion,
|
|
338
|
+
// so `{ type: 'array', minItems: 2, items: { type: 'object' } }`
|
|
339
|
+
// accepted a one-element array.
|
|
340
|
+
...hasMinItems(schema) ? [
|
|
341
|
+
` if (input.length < ${schema.minItems}) throw new Error(${quoteJsString(`[${typeName}] must have at least ${schema.minItems} items`)});`
|
|
342
|
+
] : [],
|
|
343
|
+
...hasMaxItems(schema) ? [
|
|
344
|
+
` if (input.length > ${schema.maxItems}) throw new Error(${quoteJsString(`[${typeName}] must have at most ${schema.maxItems} items`)});`
|
|
345
|
+
] : [],
|
|
346
|
+
...generateContainsCheck("input", schema, `[${typeName}]`),
|
|
347
|
+
...hasUniqueItems(schema) && schema.uniqueItems === true ? [
|
|
348
|
+
` if (!(${generateUniqueItemsCheck("input", schema)})) throw new Error(${quoteJsString(`[${typeName}] must NOT have duplicate items`)});`
|
|
349
|
+
] : [],
|
|
350
|
+
// This path delegates elements to another parser and so never reaches
|
|
351
|
+
// generateScalarStrictAssertion — `unevaluatedItems` and friends would
|
|
352
|
+
// be lost with it.
|
|
353
|
+
...generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx))
|
|
354
|
+
].join("\n") : "";
|
|
311
355
|
const delegatedBody = (itemParserName) => ` const _parsed = validateArray(input, ${itemParserName});
|
|
312
356
|
return (_parsed === input ? [..._parsed] : _parsed) as ${typeName};`;
|
|
313
357
|
const delegated = (itemParserName) => strict ? `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
@@ -330,7 +374,7 @@ ${delegatedBody(itemParserName)}
|
|
|
330
374
|
const stripUnknown = unionCtx?.stripUnknown ?? false;
|
|
331
375
|
const itemShapeValidator = generateShapeValidator(items, itemName, useRefImports, suffix, false, stripUnknown, reserved);
|
|
332
376
|
const preamble = [
|
|
333
|
-
`type ${itemName} =
|
|
377
|
+
`type ${itemName} = NonNullable<${typeName}>[number];`,
|
|
334
378
|
itemShapeValidator,
|
|
335
379
|
generateObjectParser(items, itemName, useRefImports, suffix, unionCtx?.logWarnings ?? false, strict, false, stripUnknown, unionCtx?.rootSchema, reserved, unionCtx?.caseInsensitive ?? false, itemShapeValidator)
|
|
336
380
|
].join("\n\n");
|
|
@@ -339,18 +383,29 @@ ${delegatedBody(itemParserName)}
|
|
|
339
383
|
${delegated(generateParserName(itemName))}`;
|
|
340
384
|
}
|
|
341
385
|
}
|
|
342
|
-
|
|
386
|
+
const isMultiType = isSchemaObject(schema) && Array.isArray(schema.type);
|
|
387
|
+
if (!isSchemaObject(schema) || !hasType(schema) && !isMultiType) {
|
|
388
|
+
if (strict) {
|
|
389
|
+
const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
|
|
390
|
+
if (assertion !== null) {
|
|
391
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
392
|
+
${assertion}
|
|
393
|
+
return input as ${typeName};
|
|
394
|
+
};`;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
343
397
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
344
398
|
}
|
|
345
399
|
if (!strict && schema.type === "null") {
|
|
346
400
|
return `export const ${functionName} = (input: unknown): ${typeName} => null as ${typeName};`;
|
|
347
401
|
}
|
|
348
402
|
if (strict) {
|
|
349
|
-
const assertion = generateScalarStrictAssertion(schema, typeName, unionCtx
|
|
403
|
+
const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
|
|
350
404
|
if (assertion === null) {
|
|
351
405
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
352
406
|
}
|
|
353
|
-
const
|
|
407
|
+
const arrayCast = getPrefixItems(schema) ? `[...(input as readonly unknown[])] as unknown as ${typeName}` : `[...(input as readonly unknown[])] as ${typeName}`;
|
|
408
|
+
const returnExpr = !isMultiType && schema.type === "array" ? arrayCast : `input as ${typeName}`;
|
|
354
409
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
355
410
|
${assertion}
|
|
356
411
|
return ${returnExpr};
|
|
@@ -369,7 +424,7 @@ ${assertion}
|
|
|
369
424
|
const prefixItems = getPrefixItems(schema);
|
|
370
425
|
if (prefixItems) {
|
|
371
426
|
const mapped = generatePrefixItemsMap("input", prefixItems, prefixItemsCapsLength(schema), unionCtx?.rootSchema, void 0, unionCtx?.caseInsensitive);
|
|
372
|
-
return `export const ${functionName} = (input: unknown): ${typeName} => Array.isArray(input) ? ${mapped} as ${typeName} : [] as ${typeName};`;
|
|
427
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => Array.isArray(input) ? ${mapped} as unknown as ${typeName} : [] as unknown as ${typeName};`;
|
|
373
428
|
}
|
|
374
429
|
if (hasItems(schema) && !Array.isArray(schema.items) && isCoercibleItemSchema(schema.items)) {
|
|
375
430
|
const item = schema.items;
|
|
@@ -382,17 +437,50 @@ ${assertion}
|
|
|
382
437
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
383
438
|
}
|
|
384
439
|
};
|
|
385
|
-
const strictObjectKeywordPrelude = (schema, typeName) => {
|
|
440
|
+
const strictObjectKeywordPrelude = (schema, typeName, context = {}) => {
|
|
386
441
|
if (!isSchemaObject(schema))
|
|
387
442
|
return "";
|
|
388
|
-
const lines =
|
|
443
|
+
const lines = [
|
|
444
|
+
...generateObjectKeywordChecks("input", schema, `[${typeName}]`, false, context),
|
|
445
|
+
// `required` and the size bounds are the object-level constraints these
|
|
446
|
+
// specialized parsers never had a place for: `{ type: 'object', required:
|
|
447
|
+
// ['a'] }` and `{ type: 'object', minProperties: 2 }` carry no `properties`,
|
|
448
|
+
// so they land on the empty-object / record paths, which asserted only
|
|
449
|
+
// `isObject`. Keys that *are* declared are skipped — the per-property
|
|
450
|
+
// assertions of the combined parser already demand them.
|
|
451
|
+
...requiredWithoutPropertyLines(schema, typeName),
|
|
452
|
+
...generateObjectSizeChecks("input", schema, `[${typeName}]`, false),
|
|
453
|
+
// Values behind `patternProperties` / a schema-valued `additionalProperties`:
|
|
454
|
+
// these parsers build their result by copying or coercing, so nothing
|
|
455
|
+
// rejected a wrongly-typed pattern-matched value before this.
|
|
456
|
+
...generateKeyedValueChecks("input", schema, `[${typeName}]`, context),
|
|
457
|
+
// Composition on a property-less object: these parsers delegate nothing, so
|
|
458
|
+
// `{ type: 'object', allOf: [{ $ref: … }] }` had no check at all.
|
|
459
|
+
...generateCompositionChecks("input", schema, `[${typeName}]`, context),
|
|
460
|
+
// Whatever no specialized check above reaches (`unevaluated*`, a `$ref` with
|
|
461
|
+
// constraining siblings): these parsers never run generateObjectStrictAssertion,
|
|
462
|
+
// so the backstop has to be spliced in here too.
|
|
463
|
+
...generateBackstopAssertion("input", schema, `[${typeName}]`, context)
|
|
464
|
+
];
|
|
389
465
|
return lines.length > 0 ? `
|
|
390
466
|
${lines.join("\n")}` : "";
|
|
391
467
|
};
|
|
392
|
-
const
|
|
468
|
+
const requiredWithoutPropertyLines = (schema, typeName) => {
|
|
469
|
+
if (!isSchemaObject(schema) || !hasRequired(schema))
|
|
470
|
+
return [];
|
|
471
|
+
const declared = hasProperties(schema) ? schema.properties : {};
|
|
472
|
+
const lines = [];
|
|
473
|
+
for (const key of new Set(schema.required)) {
|
|
474
|
+
if (key in declared)
|
|
475
|
+
continue;
|
|
476
|
+
lines.push(` if (${missingCheck("input", key)}) throw new Error(${quoteJsString(`[${typeName}] missing required property '${key}'`)});`);
|
|
477
|
+
}
|
|
478
|
+
return lines;
|
|
479
|
+
};
|
|
480
|
+
const generateEmptyObjectParser = (typeName, strict, schema, context = {}) => {
|
|
393
481
|
const functionName = generateParserName(typeName);
|
|
394
482
|
if (strict) {
|
|
395
|
-
const prelude = schema !== void 0 ? strictObjectKeywordPrelude(schema, typeName) : "";
|
|
483
|
+
const prelude = schema !== void 0 ? strictObjectKeywordPrelude(schema, typeName, context) : "";
|
|
396
484
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
397
485
|
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${prelude}
|
|
398
486
|
return { ...input } as ${typeName};
|
|
@@ -473,7 +561,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
473
561
|
if (!hasProperties(schema)) {
|
|
474
562
|
if (strict) {
|
|
475
563
|
return `${exportPrefix}const ${functionName} = (input: unknown): ${typeName} => {
|
|
476
|
-
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${strictObjectKeywordPrelude(schema, typeName)}
|
|
564
|
+
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${strictObjectKeywordPrelude(schema, typeName, { useRefImports, suffix, stripUnknown, ...rootSchema !== void 0 ? { rootSchema } : {} })}
|
|
477
565
|
return input as ${typeName};
|
|
478
566
|
};`;
|
|
479
567
|
}
|
|
@@ -485,7 +573,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
485
573
|
const preamble = [];
|
|
486
574
|
for (const [key, subName] of subTypeNames) {
|
|
487
575
|
const propSchema = schemaProps[key];
|
|
488
|
-
preamble.push(`type ${subName} =
|
|
576
|
+
preamble.push(`type ${subName} = NonNullable<${typeName}>[${JSON.stringify(key)}];`);
|
|
489
577
|
const subShapeValidator = generateShapeValidator(propSchema, subName, useRefImports, suffix, false, stripUnknown, reservedNames);
|
|
490
578
|
preamble.push(subShapeValidator);
|
|
491
579
|
preamble.push(generateObjectParser(propSchema, subName, useRefImports, suffix, logWarnings, strict, false, stripUnknown, rootSchema, reservedNames, caseInsensitive, subShapeValidator));
|
|
@@ -493,7 +581,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
493
581
|
for (const [key, subName] of subItemNames) {
|
|
494
582
|
const propSchema = schemaProps[key];
|
|
495
583
|
const itemSchema = propSchema.items;
|
|
496
|
-
preamble.push(`type ${subName} = NonNullable<${typeName}[${JSON.stringify(key)}]>[number];`);
|
|
584
|
+
preamble.push(`type ${subName} = NonNullable<NonNullable<${typeName}>[${JSON.stringify(key)}]>[number];`);
|
|
497
585
|
const itemShapeValidator = generateShapeValidator(itemSchema, subName, useRefImports, suffix, false, stripUnknown, reservedNames);
|
|
498
586
|
preamble.push(itemShapeValidator);
|
|
499
587
|
preamble.push(`const _every${subName} = (arr: readonly unknown[]): boolean => {
|
|
@@ -522,7 +610,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
522
610
|
const fallbackObject = generateFallbackObject(schema, useRefImports, typeName, suffix, subTypeNames);
|
|
523
611
|
const propInfo = [];
|
|
524
612
|
const hasAllOfRefParsers = useRefImports && isSchemaObject(schema) && hasAllOf(schema) && schema.allOf.some((entry) => isSchemaObject(entry) && hasRef(entry));
|
|
525
|
-
let canFastPath = !hasAllOfRefParsers && !hasFastPathBlockingKeyword(schema);
|
|
613
|
+
let canFastPath = !hasAllOfRefParsers && !hasFastPathBlockingKeyword(schema) && !(strict && inlineAllOfMembers(schema).length > 0);
|
|
526
614
|
const fastPathChecks = [];
|
|
527
615
|
const fastPathAccessorChecks = [];
|
|
528
616
|
const usedVarNames = /* @__PURE__ */ new Set();
|
|
@@ -563,6 +651,16 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
563
651
|
fastPathAccessorChecks.push(`(${accessor} === undefined || ${accessorCheck})`);
|
|
564
652
|
}
|
|
565
653
|
}
|
|
654
|
+
if (canFastPath) {
|
|
655
|
+
if (hasMinProperties(schema)) {
|
|
656
|
+
fastPathChecks.push(`Object.keys(input).length >= ${schema.minProperties}`);
|
|
657
|
+
fastPathAccessorChecks.push(`Object.keys(input).length >= ${schema.minProperties}`);
|
|
658
|
+
}
|
|
659
|
+
if (hasMaxProperties(schema)) {
|
|
660
|
+
fastPathChecks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
|
|
661
|
+
fastPathAccessorChecks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
566
664
|
if (stripKeys) {
|
|
567
665
|
fastPathChecks.push(exactKeyCount !== null ? `Object.getPrototypeOf(input) === Object.prototype && Object.keys(input).length === ${exactKeyCount}` : `_hasOnlyKnownKeys${typeName}(input)`);
|
|
568
666
|
}
|
|
@@ -697,7 +795,13 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
697
795
|
continue;
|
|
698
796
|
}
|
|
699
797
|
if (!isSchemaObject(propSchema)) {
|
|
700
|
-
if (
|
|
798
|
+
if (propSchema === true) {
|
|
799
|
+
if (isRequired) {
|
|
800
|
+
objectLines.push(` ${safeLiteralKey(key)}: ${accessor},`);
|
|
801
|
+
} else if (stripKeys) {
|
|
802
|
+
objectLines.push(` ...(${accessor} !== undefined && { ${safeLiteralKey(key)}: ${accessor} }),`);
|
|
803
|
+
}
|
|
804
|
+
} else if (isRequired) {
|
|
701
805
|
objectLines.push(` ${safeLiteralKey(key)}: undefined,`);
|
|
702
806
|
}
|
|
703
807
|
continue;
|
|
@@ -736,8 +840,12 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
736
840
|
const fields = [];
|
|
737
841
|
for (const { key, varName, isRequired, propSchema } of propInfo) {
|
|
738
842
|
if (!isSchemaObject(propSchema)) {
|
|
739
|
-
if (
|
|
843
|
+
if (propSchema === true) {
|
|
844
|
+
const acc = safeAccessor("input", key);
|
|
845
|
+
fields.push(isRequired ? ` ${safeLiteralKey(key)}: ${acc},` : ` ...(${acc} !== undefined && { ${safeLiteralKey(key)}: ${acc} }),`);
|
|
846
|
+
} else if (isRequired) {
|
|
740
847
|
fields.push(` ${safeLiteralKey(key)}: undefined,`);
|
|
848
|
+
}
|
|
741
849
|
continue;
|
|
742
850
|
}
|
|
743
851
|
const accessor = shouldCacheVariable(propSchema, canFastPath, useRefImports) ? varName : safeAccessor("input", key);
|
|
@@ -823,19 +931,19 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
823
931
|
${fn}`;
|
|
824
932
|
};
|
|
825
933
|
const safeResultAssign = (keyVar, valueExpr) => `if (${keyVar} === "__proto__") { Object.defineProperty(result, ${keyVar}, { value: ${valueExpr}, writable: true, enumerable: true, configurable: true }); } else { (result as Record<string, unknown>)[${keyVar}] = ${valueExpr}; }`;
|
|
826
|
-
const generateStrictCombinedParser = (schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict) => {
|
|
934
|
+
const generateStrictCombinedParser = (schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict, context = {}) => {
|
|
827
935
|
const declaredKeys = hasProperties(schema) ? Object.keys(schema.properties) : [];
|
|
828
936
|
const knownKeyCheck = unknownKeyCheck(declaredKeys, "_knownKeys");
|
|
829
937
|
const refPattern = useRefImports ? patterns.find(([, ps]) => isSchemaObject(ps) && hasRef(ps)) : void 0;
|
|
830
938
|
const loopLines = [` if (${knownKeyCheck.isKnown("key")}) continue;`];
|
|
831
939
|
if (refPattern) {
|
|
832
940
|
const parserName = generateParserName(refToName(refPattern[1].$ref, suffix));
|
|
833
|
-
loopLines.push(` if (
|
|
941
|
+
loopLines.push(` if (${regexLiteral(refPattern[0])}.test(key)) {`);
|
|
834
942
|
loopLines.push(` ${safeResultAssign("key", `${parserName}(input[key])`)}`);
|
|
835
943
|
loopLines.push(` continue;`);
|
|
836
944
|
loopLines.push(` }`);
|
|
837
945
|
}
|
|
838
|
-
const keepConditions = patterns.filter(([p]) => !(refPattern && p === refPattern[0])).map(([p]) =>
|
|
946
|
+
const keepConditions = patterns.filter(([p]) => !(refPattern && p === refPattern[0])).map(([p]) => `${regexLiteral(p)}.test(key)`);
|
|
839
947
|
if (keepConditions.length > 0) {
|
|
840
948
|
loopLines.push(` if (${keepConditions.join(" || ")}) {`);
|
|
841
949
|
loopLines.push(` ${safeResultAssign("key", "input[key]")}`);
|
|
@@ -850,7 +958,7 @@ ${propertyLines.join("\n")}
|
|
|
850
958
|
}` : "{}";
|
|
851
959
|
const knownKeysDeclaration = knownKeyCheck.declarations.map((decl) => ` ${decl};
|
|
852
960
|
`).join("");
|
|
853
|
-
const assertionPrelude = strict ? generateObjectStrictAssertion(schema, typeName,
|
|
961
|
+
const assertionPrelude = strict ? generateObjectStrictAssertion(schema, typeName, context).slice(1) : [];
|
|
854
962
|
const keywordPrelude = assertionPrelude.length > 0 ? `
|
|
855
963
|
${assertionPrelude.join("\n")}` : "";
|
|
856
964
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
@@ -864,7 +972,7 @@ ${loopLines.join("\n")}
|
|
|
864
972
|
return result;
|
|
865
973
|
};`;
|
|
866
974
|
};
|
|
867
|
-
const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown = false, caseInsensitive = false) => {
|
|
975
|
+
const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown = false, caseInsensitive = false, context = {}) => {
|
|
868
976
|
const functionName = generateParserName(typeName);
|
|
869
977
|
const entries = generatePropertyEntries(schema, useRefImports, suffix, caseInsensitive);
|
|
870
978
|
const propertyLines = entries.map((entry) => {
|
|
@@ -874,26 +982,26 @@ const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, l
|
|
|
874
982
|
return ` ${safeLiteralKey(entry.key)}: ${entry.value},`;
|
|
875
983
|
});
|
|
876
984
|
if (!isSchemaObject(schema) || !("patternProperties" in schema)) {
|
|
877
|
-
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown,
|
|
985
|
+
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive);
|
|
878
986
|
}
|
|
879
987
|
const patternProps = schema.patternProperties;
|
|
880
988
|
const patterns = Object.entries(patternProps);
|
|
881
989
|
const refPattern = patterns.find(([, ps]) => isSchemaObject(ps) && hasRef(ps));
|
|
882
990
|
const strictKeys = hasAdditionalProperties(schema) && schema.additionalProperties === false;
|
|
883
991
|
if (strictKeys) {
|
|
884
|
-
return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict);
|
|
992
|
+
return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict, context);
|
|
885
993
|
}
|
|
886
994
|
if (stripUnknown && !strict) {
|
|
887
|
-
return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, false);
|
|
995
|
+
return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, false, context);
|
|
888
996
|
}
|
|
889
997
|
if (!refPattern || !useRefImports) {
|
|
890
|
-
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown,
|
|
998
|
+
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive);
|
|
891
999
|
}
|
|
892
1000
|
const [pattern, patternSchema] = refPattern;
|
|
893
1001
|
const ref = patternSchema.$ref;
|
|
894
1002
|
const parserName = generateParserName(refToName(ref, suffix));
|
|
895
1003
|
const assignmentCode = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
|
|
896
|
-
const
|
|
1004
|
+
const patternLiteral = regexLiteral(pattern);
|
|
897
1005
|
const inputSpread = " ...input,";
|
|
898
1006
|
let objectProperties = inputSpread;
|
|
899
1007
|
if (propertyLines.length > 0) {
|
|
@@ -902,7 +1010,7 @@ const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, l
|
|
|
902
1010
|
}
|
|
903
1011
|
}
|
|
904
1012
|
const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
|
|
905
|
-
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName) : "";
|
|
1013
|
+
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
|
|
906
1014
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
907
1015
|
if (!isObject(input)) {
|
|
908
1016
|
${notObjectBranch}
|
|
@@ -911,7 +1019,7 @@ ${notObjectBranch}
|
|
|
911
1019
|
${objectProperties}
|
|
912
1020
|
} as unknown as ${typeName};
|
|
913
1021
|
for (const key in input) {
|
|
914
|
-
if (
|
|
1022
|
+
if (${patternLiteral}.test(key)) {
|
|
915
1023
|
const value = input[key];
|
|
916
1024
|
${assignmentCode}
|
|
917
1025
|
}
|
|
@@ -919,13 +1027,13 @@ ${objectProperties}
|
|
|
919
1027
|
return result;
|
|
920
1028
|
};`;
|
|
921
1029
|
};
|
|
922
|
-
const generateAdditionalPropertiesParser = (schema, typeName, useRefImports, suffix, strict) => {
|
|
1030
|
+
const generateAdditionalPropertiesParser = (schema, typeName, useRefImports, suffix, strict, context = {}) => {
|
|
923
1031
|
const functionName = generateParserName(typeName);
|
|
924
1032
|
const additionalProps = schema.additionalProperties;
|
|
925
1033
|
if (!additionalProps) {
|
|
926
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1034
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
927
1035
|
}
|
|
928
|
-
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName) : "";
|
|
1036
|
+
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
|
|
929
1037
|
if (useRefImports && isSchemaObject(additionalProps) && hasRef(additionalProps)) {
|
|
930
1038
|
const ref = additionalProps.$ref;
|
|
931
1039
|
const parserName = generateParserName(refToName(ref, suffix));
|
|
@@ -993,15 +1101,15 @@ const generateInlineValueParser = (schema, strict = false, label = "") => {
|
|
|
993
1101
|
return null;
|
|
994
1102
|
}
|
|
995
1103
|
};
|
|
996
|
-
const generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix, strict) => {
|
|
1104
|
+
const generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix, strict, context = {}) => {
|
|
997
1105
|
const functionName = generateParserName(typeName);
|
|
998
1106
|
if (!("patternProperties" in schema) || typeof schema.patternProperties !== "object") {
|
|
999
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1107
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1000
1108
|
}
|
|
1001
1109
|
const patternProps = schema.patternProperties;
|
|
1002
1110
|
const patterns = Object.entries(patternProps);
|
|
1003
1111
|
if (patterns.length === 0) {
|
|
1004
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1112
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1005
1113
|
}
|
|
1006
1114
|
const [pattern, patternSchema] = patterns[0];
|
|
1007
1115
|
let patternAssignment = "(result as Record<string, unknown>)[key] = value;";
|
|
@@ -1015,19 +1123,19 @@ const generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix
|
|
|
1015
1123
|
patternAssignment = "";
|
|
1016
1124
|
patternValueExpr = null;
|
|
1017
1125
|
}
|
|
1018
|
-
const
|
|
1126
|
+
const patternLiteral = regexLiteral(pattern);
|
|
1019
1127
|
const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
|
|
1020
|
-
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName) : "";
|
|
1128
|
+
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
|
|
1021
1129
|
const strictKeys = hasAdditionalProperties(schema) && schema.additionalProperties === false;
|
|
1022
1130
|
if (strictKeys) {
|
|
1023
|
-
const loopLines = [` if (
|
|
1131
|
+
const loopLines = [` if (${patternLiteral}.test(key)) {`];
|
|
1024
1132
|
if (patternValueExpr) {
|
|
1025
1133
|
loopLines.push(` const value = input[key];`);
|
|
1026
1134
|
loopLines.push(` ${safeResultAssign("key", patternValueExpr)}`);
|
|
1027
1135
|
}
|
|
1028
1136
|
loopLines.push(` continue;`);
|
|
1029
1137
|
loopLines.push(` }`);
|
|
1030
|
-
const keepConditions = patterns.slice(1).map(([p]) =>
|
|
1138
|
+
const keepConditions = patterns.slice(1).map(([p]) => `${regexLiteral(p)}.test(key)`);
|
|
1031
1139
|
if (keepConditions.length > 0) {
|
|
1032
1140
|
loopLines.push(` if (${keepConditions.join(" || ")}) {`);
|
|
1033
1141
|
loopLines.push(` ${safeResultAssign("key", "input[key]")}`);
|
|
@@ -1055,7 +1163,7 @@ ${notObjectBranch}
|
|
|
1055
1163
|
...input,
|
|
1056
1164
|
} as unknown as ${typeName};
|
|
1057
1165
|
for (const key in input) {
|
|
1058
|
-
if (
|
|
1166
|
+
if (${patternLiteral}.test(key)) {
|
|
1059
1167
|
const value = input[key];
|
|
1060
1168
|
${patternAssignment}
|
|
1061
1169
|
}
|
|
@@ -1166,6 +1274,12 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1166
1274
|
const stripUnknown = options?.stripUnknown ?? false;
|
|
1167
1275
|
const caseInsensitive = options?.caseInsensitive ?? false;
|
|
1168
1276
|
const suffix = options?.typeSuffix ?? "";
|
|
1277
|
+
const context = {
|
|
1278
|
+
useRefImports,
|
|
1279
|
+
suffix,
|
|
1280
|
+
stripUnknown,
|
|
1281
|
+
...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {}
|
|
1282
|
+
};
|
|
1169
1283
|
if (!options?.isRoot && typeName === `Schema${suffix}`) {
|
|
1170
1284
|
return generateSchemaObjectParser(typeName);
|
|
1171
1285
|
}
|
|
@@ -1182,17 +1296,39 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1182
1296
|
});
|
|
1183
1297
|
}
|
|
1184
1298
|
if (hasProperties(schema) && isSchemaObject(schema) && "patternProperties" in schema) {
|
|
1185
|
-
return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown, caseInsensitive);
|
|
1299
|
+
return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown, caseInsensitive, context);
|
|
1300
|
+
}
|
|
1301
|
+
if (strict && isSchemaObject(schema) && Array.isArray(schema.type) && schema.type.includes("null")) {
|
|
1302
|
+
return generateNonObjectParser(typeName, schema, strict, {
|
|
1303
|
+
useRefImports,
|
|
1304
|
+
suffix,
|
|
1305
|
+
stripUnknown,
|
|
1306
|
+
caseInsensitive,
|
|
1307
|
+
logWarnings,
|
|
1308
|
+
...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
|
|
1309
|
+
...options?.reservedNames !== void 0 ? { reservedNames: options.reservedNames } : {}
|
|
1310
|
+
});
|
|
1186
1311
|
}
|
|
1187
1312
|
if (hasProperties(schema)) {
|
|
1188
1313
|
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, options?.rootSchema, options?.reservedNames, caseInsensitive, options?.shapeValidatorSource);
|
|
1189
1314
|
}
|
|
1315
|
+
if (strict && isSchemaObject(schema) && ("if" in schema || "then" in schema || "else" in schema)) {
|
|
1316
|
+
return generateNonObjectParser(typeName, schema, strict, {
|
|
1317
|
+
useRefImports,
|
|
1318
|
+
suffix,
|
|
1319
|
+
stripUnknown,
|
|
1320
|
+
caseInsensitive,
|
|
1321
|
+
logWarnings,
|
|
1322
|
+
...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
|
|
1323
|
+
...options?.reservedNames !== void 0 ? { reservedNames: options.reservedNames } : {}
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1190
1326
|
if (isSchemaObject(schema) && "if" in schema && "then" in schema && "else" in schema) {
|
|
1191
1327
|
return generateConditionalParser(schema, typeName, suffix, stripUnknown, caseInsensitive);
|
|
1192
1328
|
}
|
|
1193
1329
|
const conditionalObjectSchema = getConditionalObjectSchema(schema);
|
|
1194
1330
|
if (conditionalObjectSchema) {
|
|
1195
|
-
return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown,
|
|
1331
|
+
return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive);
|
|
1196
1332
|
}
|
|
1197
1333
|
if (!isObjectLikeSchema) {
|
|
1198
1334
|
return generateNonObjectParser(typeName, schema, strict, {
|
|
@@ -1206,23 +1342,30 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1206
1342
|
});
|
|
1207
1343
|
}
|
|
1208
1344
|
if ("patternProperties" in schema) {
|
|
1209
|
-
return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict);
|
|
1345
|
+
return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict, context);
|
|
1210
1346
|
}
|
|
1211
1347
|
if ("additionalProperties" in schema && !hasProperties(schema)) {
|
|
1212
1348
|
const additionalProps = schema.additionalProperties;
|
|
1213
1349
|
if (additionalProps === true || additionalProps === false) {
|
|
1214
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1350
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1215
1351
|
}
|
|
1216
|
-
return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict);
|
|
1352
|
+
return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict, context);
|
|
1217
1353
|
}
|
|
1218
1354
|
if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
|
|
1219
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1355
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1220
1356
|
}
|
|
1221
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1357
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1222
1358
|
};
|
|
1223
|
-
const generateParserFunction = (schema, typeName,
|
|
1224
|
-
|
|
1225
|
-
|
|
1359
|
+
const generateParserFunction = (schema, typeName, rawOptions) => {
|
|
1360
|
+
const options = rawOptions?.strict && rawOptions.rootSchema === void 0 && isSchemaObject(schema) ? { ...rawOptions, rootSchema: schema } : rawOptions;
|
|
1361
|
+
if (options?.strict) {
|
|
1362
|
+
assertNoUnsupportedKeywords(schema, typeName, {
|
|
1363
|
+
useRefImports: options.useRefImports ?? false,
|
|
1364
|
+
suffix: options.typeSuffix ?? "",
|
|
1365
|
+
...options.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
|
|
1366
|
+
...options.stripUnknown !== void 0 ? { stripUnknown: options.stripUnknown } : {}
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1226
1369
|
return selectParserStrategy(schema, typeName, options);
|
|
1227
1370
|
};
|
|
1228
1371
|
const shapeValidatorFunctionName = (typeName) => shapeValidatorName(typeName);
|
|
@@ -1244,7 +1387,7 @@ const generateShapeValidator = (schema, typeName, useRefImports, suffix = "", ex
|
|
|
1244
1387
|
if (!hasProperties(schema) && !("patternProperties" in schema) && !("if" in schema) && !stripUnknown) {
|
|
1245
1388
|
const branches = getUnionBranches(schema);
|
|
1246
1389
|
if (branches) {
|
|
1247
|
-
const check = generateUnionCheck("input", branches, useRefImports, suffix);
|
|
1390
|
+
const check = generateUnionCheck("input", branches, useRefImports, suffix, isExclusiveUnion(schema));
|
|
1248
1391
|
if (check !== null) {
|
|
1249
1392
|
return `${exportPrefix}const ${fnName} = (input: unknown): boolean => ${check};`;
|
|
1250
1393
|
}
|
|
@@ -1286,6 +1429,10 @@ const generateShapeValidator = (schema, typeName, useRefImports, suffix = "", ex
|
|
|
1286
1429
|
checks.push(`(${accessor} === undefined || ${check})`);
|
|
1287
1430
|
}
|
|
1288
1431
|
}
|
|
1432
|
+
if (hasMinProperties(schema))
|
|
1433
|
+
checks.push(`Object.keys(input).length >= ${schema.minProperties}`);
|
|
1434
|
+
if (hasMaxProperties(schema))
|
|
1435
|
+
checks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
|
|
1289
1436
|
if (validatorKeyCount !== null) {
|
|
1290
1437
|
checks.push(`Object.getPrototypeOf(input) === Object.prototype && Object.keys(input).length === ${validatorKeyCount}`);
|
|
1291
1438
|
}
|