UNPKG

@stackbit/cms-sanity

Version:
283 lines 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertSchema = void 0; const _ = require('lodash'); const { omitByNil } = require('@stackbit/cms-core/dist/utils'); const { IMAGE_MODEL } = require('@stackbit/cms-core'); const { resolveLabelFieldForModel } = require('./utils'); // sanity cloudinary plugin adds these models, we are replacing them to our internal image model, so we removing them from schema response const skipModels = ['cloudinary.asset', 'cloudinary.assetDerived']; function convertSchema(schema, { stackbitModels, presets }) { // sanity schema allow arrays to be at the root level, we don't. // map all root arrays, then pass them to rest of the model and reference them as if they were inline // hope there is no cyclic references from array objects from within array objects const filteredModels = schema.models.filter((model) => !skipModels.includes(model.name)); const [arrayModels, nonArrayModels] = _.partition(filteredModels, { type: 'array' }); const arrayModelsByName = _.keyBy(arrayModels, 'name'); const models = nonArrayModels.map((model) => { const modelName = _.get(model, 'name'); const stackbitModel = _.find(stackbitModels, { name: modelName }); return mapObjectModel(model, arrayModelsByName, stackbitModel, presets); }); models.push(IMAGE_MODEL); return { models, presets }; } exports.convertSchema = convertSchema; function mapObjectModel(model, arrayModelsByName, stackbitModel, presets) { // model name can be null for nested objects const modelName = _.get(model, 'name', null); const modelLabel = _.get(model, 'title', modelName ? _.startCase(modelName) : null); const urlPath = getUrlPath(stackbitModel); const stackbitFieldGroups = _.get(stackbitModel, 'fieldGroups'); const sanityFieldGroups = _.get(model, 'groups', []).map((group) => ({ name: group.name, label: group.title })); const fields = _.get(model, 'fields', []); const mappedFields = mapObjectFields(fields, arrayModelsByName, stackbitModel); const presetsForModel = Object.keys(_.pickBy(presets, (preset) => preset.modelName === modelName)); return omitByNil({ type: getNormalizedModelType(stackbitModel, model), name: modelName, label: modelLabel, urlPath, description: null, labelField: resolveLabelFieldForModel(model, 'preview.select.title', mappedFields), fieldGroups: stackbitFieldGroups ?? sanityFieldGroups, fields: mappedFields, presets: _.isEmpty(presetsForModel) ? _.get(stackbitModel, 'presets') : presetsForModel }); } function mapObjectFields(fields, arrayModelsByName, stackbitModel) { return _.map(fields, (field) => { const fieldName = _.get(field, 'name'); const stackbitField = _.find(_.get(stackbitModel, 'fields'), { name: fieldName }); return mapField(field, arrayModelsByName, stackbitField); }); } function mapField(field, arrayModelsByName, stackbitField) { const type = _.get(field, 'type'); const name = _.get(field, 'name'); const label = _.get(field, 'title', name ? _.startCase(name) : undefined); const description = _.get(field, 'description'); const readOnly = _.get(field, 'readOnly'); const isRequired = _.get(field, 'validation.isRequired'); const options = _.get(field, 'options'); const sanityFieldGroup = _.get(field, 'group'); const stackbitFieldGroup = _.get(stackbitField, 'group'); const group = stackbitFieldGroup ?? (Array.isArray(sanityFieldGroup) ? sanityFieldGroup[0] : sanityFieldGroup); const controlType = _.get(stackbitField, 'controlType'); let hidden = _.get(field, 'hidden'); // compute default and const values let defaultValue = _.get(field, 'initialValue'); let constValue; if (isRequired) { if (!_.isUndefined(defaultValue) && hidden) { constValue = defaultValue; defaultValue = undefined; } else if (type === 'string') { const optionsList = _.get(options, 'list'); if (_.isArray(optionsList) && optionsList.length === 1) { hidden = true; // constValue = _.head(optionsList); // constValue = _.get(constValue, 'value', constValue); // defaultValue = undefined; } } } const extra = convertField(field, arrayModelsByName, stackbitField); return _.omitBy(_.assign({ type: null, name: name, label: label, description: description, group: group, controlType: controlType, required: isRequired || undefined, default: defaultValue, const: constValue, readOnly: readOnly, hidden: hidden }, extra), _.isUndefined); } function convertField(field, arrayModelsByName, stackbitField) { const type = _.get(field, 'type'); if (!_.has(fieldConverterMap, type)) { return fieldConverterMap.model(field, arrayModelsByName, stackbitField); } return fieldConverterMap[type](field, arrayModelsByName, stackbitField); } function getEnumOptions(field, stackbitField) { // A list of predefined values that the user can choose from. // The array can either include string values ['sci-fi', 'western'] // or objects [{title: 'Sci-Fi', value: 'sci-fi'}, ...] const list = _.get(field, 'options.list', []); if (!_.isEmpty(list)) { return _.map(list, (item, i) => { if (_.has(item, 'title')) { const sbOptionData = stackbitField?.options?.find((option) => option.value === item.value); return { label: item.title, value: item.value, ...sbOptionData }; } else { const sbOptionData = stackbitField?.options?.find((option) => option.value === item); return sbOptionData ? sbOptionData : item; } }); } else { return null; } } const fieldConverterMap = { string: (field, arrayModelsByName, stackbitField) => { const options = getEnumOptions(field, stackbitField); if (options) { return { type: 'enum', options: options }; } else { return { type: 'string' }; } }, slug: () => { return { type: 'slug' }; }, url: () => { return { type: 'url' }; }, text: () => { return { type: 'text' }; }, markdown: () => { return { type: 'markdown' }; }, block: () => { return { type: 'richText' }; }, span: () => { // TODO: implement when we will handle parsing of rich text }, number: (field) => { const validation = _.get(field, 'validation'); const isInteger = _.get(validation, 'isInteger'); return _.omitBy({ type: 'number', subtype: isInteger ? 'int' : 'float', min: _.get(validation, 'min'), max: _.get(validation, 'max') }, _.isNil); }, boolean: () => { return { type: 'boolean' }; }, date: () => { return { type: 'date' }; }, datetime: () => { return { type: 'datetime' }; }, file: () => { return { type: 'file' }; }, image: () => { return { type: 'image' }; }, 'cloudinary.asset': () => { return { type: 'image', source: 'cloudinary' }; }, geopoint: () => { return { type: 'geopoint' }; }, reference: (field) => { const toItems = _.castArray(_.get(field, 'to', [])); return { type: 'reference', models: _.map(toItems, (item) => item.type) }; }, object: (field, arrayModelsByName, stackbitField) => { return mapObjectModel(field, arrayModelsByName, stackbitField); }, model: (field, arrayModelsByName, stackbitField) => { const type = _.get(field, 'type'); if (_.has(arrayModelsByName, type)) { return fieldConverterMap.array(arrayModelsByName[type], arrayModelsByName, stackbitField); } return { type: 'model', models: [type] }; }, array: (field, arrayModelsByName, stackbitField) => { const options = getEnumOptions(field); if (options) { return { type: 'list', controlType: 'checkbox', items: { type: 'enum', options: options } }; } const ofItems = _.get(field, 'of', []); if (_.some(ofItems, { type: 'block' })) { return { type: 'richText' }; } const stackbitListItems = _.get(stackbitField, 'items'); const items = _.map(ofItems, (item, index) => { const stackbitListItem = Array.isArray(stackbitListItems) ? stackbitListItems[index] || _.last(stackbitListItems) : stackbitListItems; return mapField(item, arrayModelsByName, stackbitListItem); }); let modelNames = []; let referenceModelName = []; const consolidatedItems = []; _.forEach(items, (item) => { const type = _.get(item, 'type'); if (type === 'model' && _.has(item, 'models') && _.size(item) === 2) { modelNames = modelNames.concat(item.models); } else if (type === 'reference' && _.has(item, 'models') && _.size(item) === 2) { referenceModelName = referenceModelName.concat(item.models); } else { consolidatedItems.push(item); } }); if (!_.isEmpty(modelNames)) { consolidatedItems.push({ type: 'model', models: modelNames }); } if (!_.isEmpty(referenceModelName)) { consolidatedItems.push({ type: 'reference', models: referenceModelName }); } if (consolidatedItems.length === 1) { return { type: 'list', items: _.head(consolidatedItems) }; } return { type: 'list', items: consolidatedItems }; } }; function getNormalizedModelType(stackbitModel, sanityModel) { if (stackbitModel) { return stackbitModel.type === 'config' ? 'data' : stackbitModel.type; } // sanity model type can be 'document', 'object' const modelType = _.get(sanityModel, 'type'); return modelType === 'document' ? 'data' : 'object'; } function getUrlPath(stackbitModel) { const modelType = getNormalizedModelType(stackbitModel); if (modelType === 'page') { return _.get(stackbitModel, 'urlPath', '/{slug}'); } return null; } //# sourceMappingURL=sanity-schema-legacy-converter.js.map