UNPKG

@stackbit/sdk

Version:
299 lines (264 loc) 9.58 kB
import _ from 'lodash'; import { FIELD_TYPES } from '../config/config-consts'; import { Model, ObjectModel, DataModel, PageModel, ConfigModel, YamlModel, YamlObjectModel, YamlPageModel, YamlDataModel, YamlDataModelList, Field, FieldSpecificProps, FieldEnumProps, FieldList, FieldListItems, FieldListModel, FieldListObject, FieldListReference, FieldModelProps, FieldObjectProps, FieldReferenceProps, FieldCrossReferenceProps, DataModelList, FieldListProps } from '../config/config-types'; export function getModelByName(models: Model[], modelName: string): Model | undefined { return models.find((model) => model.name === modelName); } export function isConfigModel(model: Model): model is ConfigModel { return model.type === 'config'; } export function isDataModel(model: Model | YamlModel): model is DataModel | YamlDataModel { return model.type === 'data'; } export function isListDataModel(model: Model | YamlModel): model is DataModelList | YamlDataModelList { return isDataModel(model) && Boolean(model.isList); } export function isPageModel(model: Model | YamlModel): model is PageModel | YamlPageModel { return model.type === 'page'; } export function isObjectModel(model: Model | YamlModel): model is ObjectModel | YamlObjectModel { return model.type === 'object'; } export function isSingleInstanceModel(model: Model | YamlModel): boolean { if (model.type === 'config') { return true; } else if (model.type === 'data') { return _.has(model, 'file') || _.get(model, 'singleInstance', false); } else if (model.type === 'page') { return _.has(model, 'file') || _.get(model, 'singleInstance', false); } return false; } export function isObjectField(field: FieldSpecificProps): field is FieldObjectProps { return field.type === 'object'; } export function isModelField(field: FieldSpecificProps): field is FieldModelProps { return field.type === 'model'; } export function isReferenceField(field: FieldSpecificProps): field is FieldReferenceProps { return field.type === 'reference'; } export function isCrossReferenceField(field: FieldSpecificProps): field is FieldCrossReferenceProps { return field.type === 'cross-reference'; } export function isCustomModelField(field: FieldSpecificProps, modelsByName: Record<string, Model>) { // custom model field types are deprecated return !FIELD_TYPES.includes(field.type) && _.has(modelsByName, field.type); } export function isEnumField(field: FieldSpecificProps): field is FieldEnumProps { return field.type === 'enum'; } export function isListOfObjectsField(field: Field): field is FieldListObject { return isListField(field) && isObjectListItems(getListFieldItems(field)); } export function isListOfModelsField(field: Field): field is FieldListModel { return isListField(field) && isModelListItems(getListFieldItems(field)); } export function isListOfReferencesField(field: Field): field is FieldListReference { return isListField(field) && isReferenceListItems(getListFieldItems(field)); } export function isListOfCustomModelsField(field: Field, modelsByName?: Record<string, Model>): field is FieldList { // custom model field types are deprecated return isListField(field) && isCustomModelListItems(getListFieldItems(field), modelsByName); } export function isListField(field: Field): field is FieldList { // 'array' is deprecated field type return ['list', 'array'].includes(field.type); } export function isObjectListItems(items: FieldListItems): items is FieldObjectProps { return items.type === 'object'; } export function isModelListItems(items: FieldListItems): items is FieldModelProps { return items.type === 'model'; } export function isReferenceListItems(items: FieldListItems): items is FieldReferenceProps { return items.type === 'reference'; } export function isCustomModelListItems(items: FieldListItems, modelsByName?: Record<string, Model>) { // custom model field types are deprecated return !FIELD_TYPES.includes(items.type) && (!modelsByName || _.has(modelsByName, items.type)); } /** * Gets a list field and returns its items field. If list field does not define * items field, the default field is string: * * @example * listItemField = getListFieldItems({ * type: 'list', * name: '...', * items: { type: 'object', fields: [] } * } * listItemField => { * type: 'object', * name: '...', * fields: [] * } * * // list field without `items` * listItemField = getListFieldItems({ type: 'list', name: '...' } * listItemField => { type: 'string' } * * @param {Object} field * @return {Object} */ export function getListFieldItems(field: FieldList): FieldListItems { // items.type defaults to string return Object.assign({ type: 'string' }, field.items); } export function normalizeListField(field: FieldList): FieldList { // in older versions, the list 'items' were optional and when weren't // specified a default list of strings was used. if (_.get(field, 'items.type')) { return field; } return { ...field, items: { ...(field.items ?? {}), type: 'string' } } as FieldList; } export function normalizeListFieldInPlace(field: FieldList | DataModelList): FieldList | DataModelList { // 'items.type' of list field default to 'string', set it explicitly if (!_.has(field, 'items.type')) { _.set(field, 'items.type', 'string'); } return field; } export function getListItemsOrSelf(field: Field): Exclude<FieldSpecificProps, FieldListProps> { if (isListField(field)) { return normalizeListField(field).items; } return field; } export function mapListItemsPropsOrSelfSpecificProps( field: Field, func: <T extends Exclude<FieldSpecificProps, FieldListProps>>(listItemsPropsOrField: T) => T ): Field { if (isListField(field)) { const listItems = normalizeListField(field).items; const mappedListItems = func(listItems); return { ...field, items: mappedListItems } as FieldList; } return func(field); } export function assignLabelFieldIfNeeded(modelOrField: Model | FieldObjectProps) { if (modelOrField.labelField) { return; } const labelField = resolveLabelFieldForModel(modelOrField); if (labelField) { modelOrField.labelField = labelField; } } export function resolveLabelFieldForModel(modelOrField: Model | FieldObjectProps) { const fields = _.get(modelOrField, 'fields'); let labelField = _.get(modelOrField, 'labelField', null); if (labelField) { return labelField; } // see if there is a field named 'title' let titleField = _.find(fields, (field) => field.name === 'title' && ['string', 'text'].includes(field.type)); if (!titleField) { // see if there is a field named 'label' titleField = _.find(fields, (field) => field.name === 'label' && ['string', 'text'].includes(field.type)); } if (!titleField) { // get the first 'string' field titleField = _.find(fields, { type: 'string' }); } if (titleField) { labelField = _.get(titleField, 'name'); } return labelField || null; } export function getModelFieldForModelKeyPath(model: Model | YamlModel, modelKeyPath: string[]) { function _getField(field: Field, modelKeyPath: string[]): Model | YamlModel | Field | FieldListItems | FieldObjectProps | null { if (modelKeyPath.length === 0) { return field; } else if (isObjectField(field)) { return _getObjectFields(field, modelKeyPath); } else if (isListField(field)) { return _getListItems(field, modelKeyPath); } else { return null; } } function _getObjectFields<T extends FieldObjectProps | Model | YamlModel>( field: T, modelKeyPath: string[] ): Model | YamlModel | Field | FieldListItems | FieldObjectProps | null { if (modelKeyPath.length === 0) { return field; } const key = modelKeyPath.shift(); if (key !== 'fields' || !field.fields) { return null; } if (modelKeyPath.length === 0) { return null; } const fieldName = modelKeyPath.shift(); const childField = _.find(field.fields, (field) => field.name === fieldName); if (!childField) { return null; } return _getField(childField, modelKeyPath); } function _getListItems<T extends FieldList | DataModelList | YamlDataModelList>( field: T, modelKeyPath: string[] ): Model | YamlModel | Field | FieldListItems | FieldObjectProps | null { if (modelKeyPath.length === 0) { return field; } const key = modelKeyPath.shift(); if (key !== 'items' || !field.items) { return null; } if (modelKeyPath.length === 0) { return field.items; } if (!isObjectListItems(field.items)) { return null; } return _getObjectFields(field.items, modelKeyPath); } modelKeyPath = modelKeyPath.slice(); if (!model) { return null; } else if (isListDataModel(model)) { return _getListItems(model, modelKeyPath); } else { return _getObjectFields(model, modelKeyPath); } }