"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateTypes = void 0;
/* eslint-disable no-nested-ternary */
const fs_1 = __importDefault(require("fs"));
const json_schema_to_typescript_1 = require("json-schema-to-typescript");
const logger_1 = __importDefault(require("../utilities/logger"));
const types_1 = require("../fields/config/types");
const load_1 = __importDefault(require("../config/load"));
const deepCopyObject_1 = __importDefault(require("../utilities/deepCopyObject"));
const nonOptionalFieldTypes = ['group', 'array', 'blocks'];
const propertyIsOptional = (field) => {
    return (0, types_1.fieldAffectsData)(field) && (field.required === true || nonOptionalFieldTypes.includes(field.type));
};
function getCollectionIDType(collections, slug) {
    const matchedCollection = collections.find((collection) => collection.slug === slug);
    const customIdField = matchedCollection.fields.find((field) => 'name' in field && field.name === 'id');
    if (customIdField && customIdField.type === 'number') {
        return 'number';
    }
    return 'string';
}
function returnOptionEnums(options) {
    return options.map((option) => {
        if (typeof option === 'object' && 'value' in option) {
            return option.value;
        }
        return option;
    });
}
function generateFieldTypes(config, fields) {
    let topLevelProps = [];
    let requiredTopLevelProps = [];
    return {
        properties: Object.fromEntries(fields.reduce((properties, field) => {
            let fieldSchema;
            switch (field.type) {
                case 'text':
                case 'textarea':
                case 'code':
                case 'email':
                case 'date': {
                    fieldSchema = { type: 'string' };
                    break;
                }
                case 'number': {
                    fieldSchema = { type: 'number' };
                    break;
                }
                case 'checkbox': {
                    fieldSchema = { type: 'boolean' };
                    break;
                }
                case 'richText': {
                    fieldSchema = {
                        type: 'array',
                        items: {
                            type: 'object',
                        },
                    };
                    break;
                }
                case 'radio': {
                    fieldSchema = {
                        type: 'string',
                        enum: returnOptionEnums(field.options),
                    };
                    break;
                }
                case 'select': {
                    const selectType = {
                        type: 'string',
                        enum: returnOptionEnums(field.options),
                    };
                    if (field.hasMany) {
                        fieldSchema = {
                            type: 'array',
                            items: selectType,
                        };
                    }
                    else {
                        fieldSchema = selectType;
                    }
                    break;
                }
                case 'point': {
                    fieldSchema = {
                        type: 'array',
                        minItems: 2,
                        maxItems: 2,
                        items: [
                            {
                                type: 'number',
                            },
                            {
                                type: 'number',
                            },
                        ],
                    };
                    break;
                }
                case 'relationship': {
                    if (Array.isArray(field.relationTo)) {
                        if (field.hasMany) {
                            fieldSchema = {
                                type: 'array',
                                items: {
                                    oneOf: field.relationTo.map((relation) => {
                                        const idFieldType = getCollectionIDType(config.collections, relation);
                                        return {
                                            type: 'object',
                                            additionalProperties: false,
                                            properties: {
                                                value: {
                                                    oneOf: [
                                                        {
                                                            type: idFieldType,
                                                        },
                                                        {
                                                            $ref: `#/definitions/${relation}`,
                                                        },
                                                    ],
                                                },
                                                relationTo: {
                                                    const: relation,
                                                },
                                            },
                                            required: ['value', 'relationTo'],
                                        };
                                    }),
                                },
                            };
                        }
                        else {
                            fieldSchema = {
                                oneOf: field.relationTo.map((relation) => {
                                    const idFieldType = getCollectionIDType(config.collections, relation);
                                    return {
                                        type: 'object',
                                        additionalProperties: false,
                                        properties: {
                                            value: {
                                                oneOf: [
                                                    {
                                                        type: idFieldType,
                                                    },
                                                    {
                                                        $ref: `#/definitions/${relation}`,
                                                    },
                                                ],
                                            },
                                            relationTo: {
                                                const: relation,
                                            },
                                        },
                                        required: ['value', 'relationTo'],
                                    };
                                }),
                            };
                        }
                    }
                    else {
                        const idFieldType = getCollectionIDType(config.collections, field.relationTo);
                        if (field.hasMany) {
                            fieldSchema = {
                                type: 'array',
                                items: {
                                    oneOf: [
                                        {
                                            type: idFieldType,
                                        },
                                        {
                                            $ref: `#/definitions/${field.relationTo}`,
                                        },
                                    ],
                                },
                            };
                        }
                        else {
                            fieldSchema = {
                                oneOf: [
                                    {
                                        type: idFieldType,
                                    },
                                    {
                                        $ref: `#/definitions/${field.relationTo}`,
                                    },
                                ],
                            };
                        }
                    }
                    break;
                }
                case 'upload': {
                    const idFieldType = getCollectionIDType(config.collections, field.relationTo);
                    fieldSchema = {
                        oneOf: [
                            {
                                type: idFieldType,
                            },
                            {
                                $ref: `#/definitions/${field.relationTo}`,
                            },
                        ],
                    };
                    break;
                }
                case 'blocks': {
                    fieldSchema = {
                        type: 'array',
                        items: {
                            oneOf: field.blocks.map((block) => {
                                const blockSchema = generateFieldTypes(config, block.fields);
                                return {
                                    type: 'object',
                                    additionalProperties: false,
                                    properties: {
                                        ...blockSchema.properties,
                                        blockType: {
                                            const: block.slug,
                                        },
                                    },
                                    required: [
                                        'blockType',
                                        ...blockSchema.required,
                                    ],
                                };
                            }),
                        },
                    };
                    break;
                }
                case 'array': {
                    fieldSchema = {
                        type: 'array',
                        items: {
                            type: 'object',
                            additionalProperties: false,
                            ...generateFieldTypes(config, field.fields),
                        },
                    };
                    break;
                }
                case 'row':
                case 'collapsible': {
                    const topLevelFields = generateFieldTypes(config, field.fields);
                    requiredTopLevelProps = requiredTopLevelProps.concat(topLevelFields.required);
                    topLevelProps = topLevelProps.concat(Object.entries(topLevelFields.properties).map((prop) => prop));
                    break;
                }
                case 'tabs': {
                    field.tabs.forEach((tab) => {
                        const topLevelFields = generateFieldTypes(config, tab.fields);
                        requiredTopLevelProps = requiredTopLevelProps.concat(topLevelFields.required);
                        topLevelProps = topLevelProps.concat(Object.entries(topLevelFields.properties).map((prop) => prop));
                    });
                    break;
                }
                case 'group': {
                    fieldSchema = {
                        type: 'object',
                        additionalProperties: false,
                        ...generateFieldTypes(config, field.fields),
                    };
                    break;
                }
                default: {
                    break;
                }
            }
            if (fieldSchema && (0, types_1.fieldAffectsData)(field)) {
                return [
                    ...properties,
                    [
                        field.name,
                        {
                            ...fieldSchema,
                        },
                    ],
                ];
            }
            return [
                ...properties,
                ...topLevelProps,
            ];
        }, [])),
        required: [
            ...fields
                .filter(propertyIsOptional)
                .map((field) => ((0, types_1.fieldAffectsData)(field) ? field.name : '')),
            ...requiredTopLevelProps,
        ],
    };
}
function entityToJsonSchema(config, incomingEntity) {
    const entity = (0, deepCopyObject_1.default)(incomingEntity);
    const title = 'label' in entity ? entity.label : entity.labels.singular;
    const idField = { type: 'text', name: 'id', required: true };
    const customIdField = entity.fields.find((field) => (0, types_1.fieldAffectsData)(field) && field.name === 'id');
    if (customIdField) {
        customIdField.required = true;
    }
    else {
        entity.fields.unshift(idField);
    }
    if ('timestamps' in entity && entity.timestamps !== false) {
        entity.fields.push({
            type: 'text',
            name: 'createdAt',
            required: true,
        }, {
            type: 'text',
            name: 'updatedAt',
            required: true,
        });
    }
    return {
        title,
        type: 'object',
        additionalProperties: false,
        ...generateFieldTypes(config, entity.fields),
    };
}
function configToJsonSchema(config) {
    return {
        definitions: Object.fromEntries([
            ...config.globals.map((global) => [
                global.slug,
                entityToJsonSchema(config, global),
            ]),
            ...config.collections.map((collection) => [
                collection.slug,
                entityToJsonSchema(config, collection),
            ]),
        ]),
        additionalProperties: false,
    };
}
function generateTypes() {
    const logger = (0, logger_1.default)();
    const config = (0, load_1.default)();
    logger.info('Compiling TS types for Collections and Globals...');
    const jsonSchema = configToJsonSchema(config);
    (0, json_schema_to_typescript_1.compile)(jsonSchema, 'Config', {
        unreachableDefinitions: true,
        bannerComment: '/* tslint:disable */\n/**\n* This file was automatically generated by Payload CMS.\n* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,\n* and re-run `payload generate:types` to regenerate this file.\n*/',
        style: {
            singleQuote: true,
        },
    }).then((compiled) => {
        fs_1.default.writeFileSync(config.typescript.outputFile, compiled);
        logger.info(`Types written to ${config.typescript.outputFile}`);
    });
}
exports.generateTypes = generateTypes;
// when generateTypes.js is launched directly
if (module.id === require.main.id) {
    generateTypes();
}
//# sourceMappingURL=data:application/json;base64, 