@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
283 lines (282 loc) • 13.1 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const zod_1 = require("zod");
const Utilities_1 = __importDefault(require("../core/Utilities"));
const Database_1 = __importDefault(require("../minecraft/Database"));
const IField_1 = require("./IField");
const DataFormUtilities_1 = __importDefault(require("./DataFormUtilities"));
class DataFormZod {
static getZodSchema(formDefinition) {
const cache = new WeakMap();
return DataFormZod.buildZodSchema(formDefinition, cache);
}
static buildZodSchema(formDefinition, cache) {
if (cache.has(formDefinition)) {
return cache.get(formDefinition);
}
const placeholder = zod_1.z.lazy(() => cache.get(formDefinition) ?? zod_1.z.unknown());
cache.set(formDefinition, placeholder);
const fieldSchemas = {};
if (Array.isArray(formDefinition.fields)) {
for (const field of formDefinition.fields) {
if (!field ||
!field.id ||
!Utilities_1.default.isUsableAsObjectKey(field.id) ||
field.id === "__scalar" ||
field.id.indexOf("<") >= 0 ||
field.id.indexOf(">") >= 0) {
continue;
}
fieldSchemas[field.id] = DataFormZod.getFieldSchema(field, cache);
}
}
const baseObject = zod_1.z.object(fieldSchemas);
const objectSchema = formDefinition.customField
? baseObject.catchall(DataFormZod.getFieldSchema(formDefinition.customField, cache, {
skipOptionalWrap: true,
}))
: baseObject.passthrough();
let finalSchema = objectSchema;
if (formDefinition.scalarField && !formDefinition.scalarFieldUpgradeName) {
const scalarSchema = DataFormZod.getFieldSchema(formDefinition.scalarField, cache, {
skipOptionalWrap: true,
});
finalSchema = finalSchema.or(scalarSchema);
}
cache.set(formDefinition, finalSchema);
return finalSchema;
}
static getFieldSchema(field, cache, options) {
const variants = DataFormUtilities_1.default.getFieldAndAlternates(field);
const schemas = [];
for (const variant of variants) {
const schema = DataFormZod.getSingleFieldSchema(variant, cache);
if (schema) {
schemas.push(schema);
}
}
let combined = DataFormZod.combineSchemas(schemas);
if (!options?.skipOptionalWrap) {
const isRequired = variants.some((variant) => variant.isRequired);
if (!isRequired) {
combined = combined.optional();
}
}
return combined;
}
static combineSchemas(schemas) {
if (!schemas.length) {
return zod_1.z.unknown();
}
let combined = schemas[0];
for (let i = 1; i < schemas.length; i++) {
combined = combined.or(schemas[i]);
}
return combined;
}
static getSingleFieldSchema(field, cache) {
switch (field.dataType) {
case IField_1.FieldDataType.boolean:
return zod_1.z.boolean();
case IField_1.FieldDataType.intBoolean: {
const booleanNumber = DataFormZod.createNumberSchema(field, { integer: true }).min(0).max(1);
return booleanNumber.or(zod_1.z.boolean());
}
case IField_1.FieldDataType.int:
case IField_1.FieldDataType.intEnum:
case IField_1.FieldDataType.intValueLookup: {
if (field.dataType === IField_1.FieldDataType.intEnum && field.choices && field.choices.length > 0) {
const numericChoices = field.choices
.map((choice) => choice.id)
.filter((id) => typeof id === "number");
if (numericChoices.length > 0) {
let literalSchema = zod_1.z.literal(numericChoices[0]);
for (let i = 1; i < numericChoices.length; i++) {
literalSchema = literalSchema.or(zod_1.z.literal(numericChoices[i]));
}
return literalSchema;
}
}
return DataFormZod.createNumberSchema(field, { integer: true });
}
case IField_1.FieldDataType.float:
case IField_1.FieldDataType.number:
case IField_1.FieldDataType.long:
return DataFormZod.createNumberSchema(field);
case IField_1.FieldDataType.percentRange:
return DataFormZod.createRangeSchema(field, { clamp01: true });
case IField_1.FieldDataType.intRange:
return DataFormZod.createRangeSchema(field, { integer: true });
case IField_1.FieldDataType.floatRange:
return DataFormZod.createRangeSchema(field);
case IField_1.FieldDataType.stringEnum: {
if (field.choices && field.choices.length > 0) {
const stringChoices = field.choices
.map((choice) => choice.id)
.filter((id) => typeof id === "string");
if (stringChoices.length > 0) {
return zod_1.z.enum(stringChoices);
}
}
return DataFormZod.createStringSchema(field);
}
case IField_1.FieldDataType.string:
case IField_1.FieldDataType.stringLookup:
case IField_1.FieldDataType.longFormString:
case IField_1.FieldDataType.molang:
case IField_1.FieldDataType.localizableString:
return DataFormZod.createStringSchema(field);
case IField_1.FieldDataType.uuid:
return DataFormZod.createStringSchema(field).uuid();
case IField_1.FieldDataType.longFormStringArray:
case IField_1.FieldDataType.stringArray:
case IField_1.FieldDataType.checkboxListAsStringArray:
case IField_1.FieldDataType.molangArray:
return DataFormZod.createArraySchema(zod_1.z.string(), field);
case IField_1.FieldDataType.twoDStringArray:
return DataFormZod.createArraySchema(zod_1.z.array(zod_1.z.string()), field);
case IField_1.FieldDataType.numberArray:
return DataFormZod.createArraySchema(zod_1.z.number(), field);
case IField_1.FieldDataType.point2:
return DataFormZod.createVectorSchema(2, field);
case IField_1.FieldDataType.point3:
return DataFormZod.createVectorSchema(3, field);
case IField_1.FieldDataType.intPoint3:
case IField_1.FieldDataType.location:
case IField_1.FieldDataType.locationOffset:
return DataFormZod.createVectorSchema(3, field, { integer: field.dataType === IField_1.FieldDataType.intPoint3 });
case IField_1.FieldDataType.keyedStringCollection:
return zod_1.z.record(zod_1.z.string());
case IField_1.FieldDataType.keyedBooleanCollection:
return zod_1.z.record(zod_1.z.boolean());
case IField_1.FieldDataType.keyedStringArrayCollection:
return zod_1.z.record(zod_1.z.array(zod_1.z.string()));
case IField_1.FieldDataType.keyedNumberCollection:
return zod_1.z.record(zod_1.z.number());
case IField_1.FieldDataType.keyedNumberArrayCollection:
return zod_1.z.record(zod_1.z.array(zod_1.z.number()));
case IField_1.FieldDataType.arrayOfKeyedStringCollection:
return DataFormZod.createArraySchema(zod_1.z.record(zod_1.z.string()), field);
case IField_1.FieldDataType.keyedKeyedStringArrayCollection:
return zod_1.z.record(zod_1.z.record(zod_1.z.array(zod_1.z.string())));
case IField_1.FieldDataType.keyedObjectCollection: {
const subSchema = DataFormZod.getSubFormSchema(field, cache) ?? zod_1.z.record(zod_1.z.unknown());
return zod_1.z.record(subSchema);
}
case IField_1.FieldDataType.objectArray: {
const subSchema = DataFormZod.getSubFormSchema(field, cache) ?? zod_1.z.record(zod_1.z.unknown());
return DataFormZod.createArraySchema(subSchema, field);
}
case IField_1.FieldDataType.object: {
return DataFormZod.getSubFormSchema(field, cache) ?? zod_1.z.record(zod_1.z.unknown());
}
case IField_1.FieldDataType.minecraftFilter:
case IField_1.FieldDataType.minecraftEventTrigger:
return zod_1.z.record(zod_1.z.unknown());
case IField_1.FieldDataType.minecraftEventTriggerArray:
return DataFormZod.createArraySchema(zod_1.z.record(zod_1.z.unknown()), field);
case IField_1.FieldDataType.minecraftEventReference:
return DataFormZod.createStringSchema(field);
case IField_1.FieldDataType.version: {
const tupleSchema = zod_1.z.tuple([zod_1.z.number().int(), zod_1.z.number().int(), zod_1.z.number().int()]);
return zod_1.z.string().or(zod_1.z.number()).or(tupleSchema);
}
default:
return zod_1.z.unknown();
}
}
static createNumberSchema(field, options) {
let schema = options?.integer ? zod_1.z.number().int() : zod_1.z.number();
if (options?.clamp01) {
schema = schema.min(0).max(1);
}
if (field.minValue !== undefined) {
schema = schema.min(field.minValue);
}
if (field.maxValue !== undefined) {
schema = schema.max(field.maxValue);
}
return schema;
}
static createStringSchema(field) {
let schema = zod_1.z.string();
if (field.minLength !== undefined) {
schema = schema.min(field.minLength);
}
if (field.maxLength !== undefined) {
schema = schema.max(field.maxLength);
}
return schema;
}
static createArraySchema(elementType, field) {
let schema = zod_1.z.array(elementType);
if (field.fixedLength !== undefined) {
schema = schema.length(field.fixedLength);
}
else {
if (field.minLength !== undefined) {
schema = schema.min(field.minLength);
}
if (field.maxLength !== undefined) {
schema = schema.max(field.maxLength);
}
}
return schema;
}
static createRangeSchema(field, options) {
const numberSchema = DataFormZod.createNumberSchema(field, options);
const tupleSchema = zod_1.z.tuple([numberSchema, numberSchema]);
const objectSchema = zod_1.z.object({
min: numberSchema.optional(),
max: numberSchema.optional(),
});
return numberSchema.or(tupleSchema).or(objectSchema);
}
static createVectorSchema(dimension, field, options) {
const numberFactory = () => DataFormZod.createNumberSchema(field, { integer: options?.integer });
if (dimension === 2) {
const tupleSchema = zod_1.z.tuple([numberFactory(), numberFactory()]);
const objectSchema = zod_1.z
.object({
x: numberFactory(),
y: numberFactory(),
})
.passthrough();
const altObjectSchema = zod_1.z
.object({
a: numberFactory(),
b: numberFactory(),
})
.passthrough();
return tupleSchema.or(objectSchema).or(altObjectSchema);
}
if (dimension === 3) {
const tupleSchema = zod_1.z.tuple([numberFactory(), numberFactory(), numberFactory()]);
const objectSchema = zod_1.z
.object({
x: numberFactory(),
y: numberFactory(),
z: numberFactory(),
})
.passthrough();
return tupleSchema.or(objectSchema);
}
return zod_1.z.array(numberFactory()).length(dimension);
}
static getSubFormSchema(field, cache) {
let subForm = field.subForm;
if (!subForm && field.subFormId) {
subForm = Database_1.default.getFormByPath(field.subFormId);
}
if (!subForm) {
return undefined;
}
return DataFormZod.buildZodSchema(subForm, cache);
}
}
exports.default = DataFormZod;