@stackbit/cms-core 0.1.10 → 0.1.11

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.
@@ -0,0 +1,254 @@
1
+ import _ from 'lodash';
2
+
3
+ import * as ContentStoreTypes from '../content-store-types';
4
+ import { getDocumentFieldForLocale } from '../content-store-utils';
5
+ import { IMAGE_MODEL } from '../common/common-schema';
6
+
7
+ export function mergeObjectWithDocument({
8
+ object,
9
+ document,
10
+ locale,
11
+ documentMap,
12
+ seenReferences = [],
13
+ referenceBehavior,
14
+ duplicatableModels,
15
+ nonDuplicatableModels
16
+ }: {
17
+ object: Record<string, any> | undefined;
18
+ document: ContentStoreTypes.Document;
19
+ locale?: string;
20
+ documentMap: Record<string, ContentStoreTypes.Document>;
21
+ seenReferences?: string[];
22
+ referenceBehavior?: 'copyReference' | 'duplicateContents';
23
+ duplicatableModels?: string[];
24
+ nonDuplicatableModels?: string[];
25
+ }): Record<string, any> {
26
+ return mergeObjectWithDocumentFields({
27
+ object,
28
+ documentFields: document.fields,
29
+ locale,
30
+ documentMap,
31
+ seenReferences: seenReferences.concat(document.srcObjectId),
32
+ referenceBehavior,
33
+ duplicatableModels,
34
+ nonDuplicatableModels
35
+ });
36
+ }
37
+
38
+ type Context = {
39
+ locale?: string;
40
+ documentMap: Record<string, ContentStoreTypes.Document>;
41
+ seenReferences: string[];
42
+ referenceBehavior?: 'copyReference' | 'duplicateContents';
43
+ duplicatableModels?: string[];
44
+ nonDuplicatableModels?: string[];
45
+ };
46
+
47
+ function mergeObjectWithDocumentFields({
48
+ object,
49
+ documentFields,
50
+ ...context
51
+ }: {
52
+ object: Record<string, any> | undefined;
53
+ documentFields: Record<string, ContentStoreTypes.DocumentField>;
54
+ } & Context): Record<string, any> {
55
+ return _.reduce(
56
+ documentFields,
57
+ (object, documentField, fieldName) => {
58
+ const value = mergeObjectWithDocumentField({
59
+ value: object[fieldName],
60
+ documentField,
61
+ ...context
62
+ });
63
+ if (typeof value !== 'undefined') {
64
+ object[fieldName] = value;
65
+ }
66
+ return object;
67
+ },
68
+ Object.assign({}, object)
69
+ );
70
+ }
71
+
72
+ function mergeObjectWithDocumentField({
73
+ value,
74
+ documentField,
75
+ ...context
76
+ }: {
77
+ value: any;
78
+ documentField: ContentStoreTypes.DocumentField;
79
+ } & Context): any {
80
+ const locale = context.locale;
81
+ switch (documentField.type) {
82
+ case 'string':
83
+ case 'text':
84
+ case 'html':
85
+ case 'slug':
86
+ case 'url':
87
+ case 'color':
88
+ case 'boolean':
89
+ case 'number':
90
+ case 'date':
91
+ case 'datetime':
92
+ case 'enum':
93
+ case 'file':
94
+ case 'json':
95
+ case 'style':
96
+ case 'markdown':
97
+ case 'richText': {
98
+ // if a value was provided explicitly, use it to override the value
99
+ // of the matching field in the duplicated document.
100
+ if (typeof value !== 'undefined') {
101
+ return value;
102
+ }
103
+ const localizedField = getDocumentFieldForLocale(documentField, locale);
104
+ if (localizedField) {
105
+ return localizedField.value;
106
+ }
107
+ break;
108
+ }
109
+ case 'image': {
110
+ const localizedField = getDocumentFieldForLocale(documentField, locale);
111
+ if (localizedField && !localizedField.isUnset) {
112
+ return mergeObjectWithDocumentFields({
113
+ object: value,
114
+ documentFields: localizedField.fields,
115
+ ...context
116
+ });
117
+ }
118
+ break;
119
+ }
120
+ case 'object': {
121
+ const localizedField = getDocumentFieldForLocale(documentField, locale);
122
+ if (localizedField && !localizedField.isUnset) {
123
+ return mergeObjectWithDocumentFields({
124
+ object: value,
125
+ documentFields: localizedField.fields,
126
+ ...context
127
+ });
128
+ }
129
+ break;
130
+ }
131
+ case 'model': {
132
+ const localizedField = getDocumentFieldForLocale(documentField, locale);
133
+ if (localizedField && !localizedField.isUnset) {
134
+ if (value && value.$$type !== localizedField.srcModelName) {
135
+ // if the override object's $$type isn't equal to the type
136
+ // of the current object in the field, then use whatever
137
+ // was passed in the object.
138
+ break;
139
+ }
140
+ return {
141
+ $$type: localizedField.srcModelName,
142
+ ...mergeObjectWithDocumentFields({
143
+ object: value,
144
+ documentFields: localizedField.fields,
145
+ ...context
146
+ })
147
+ };
148
+ }
149
+ break;
150
+ }
151
+ case 'reference': {
152
+ const localizedField = getDocumentFieldForLocale(documentField, locale);
153
+ if (localizedField && !localizedField.isUnset) {
154
+ if (value && value.$$ref) {
155
+ // if the override object has $$ref, use it
156
+ break;
157
+ }
158
+ if (localizedField.refType === 'asset') {
159
+ return {
160
+ $$ref: localizedField.refId
161
+ };
162
+ } else {
163
+ const document = context.documentMap[localizedField.refId];
164
+ if (!document) {
165
+ break;
166
+ }
167
+ const shouldDuplicateDocument = shouldDuplicate({
168
+ referenceField: localizedField,
169
+ modelName: document.srcModelName,
170
+ seenReferences: context.seenReferences,
171
+ referenceBehavior: context.referenceBehavior,
172
+ duplicatableModels: context.duplicatableModels,
173
+ nonDuplicatableModels: context.nonDuplicatableModels
174
+ });
175
+ if (shouldDuplicateDocument || (value && value.$$type)) {
176
+ if (value && value.$$type !== document.srcModelName) {
177
+ // if the override object has $$type different from
178
+ // the type of the document that is currently
179
+ // referenced in the field, then create and link a
180
+ // new document according to the provided object
181
+ break;
182
+ }
183
+ return {
184
+ $$type: document.srcModelName,
185
+ ...mergeObjectWithDocument({
186
+ object: value,
187
+ document,
188
+ locale: context.locale,
189
+ documentMap: context.documentMap,
190
+ seenReferences: context.seenReferences.concat(document.srcObjectId),
191
+ referenceBehavior: context.referenceBehavior,
192
+ duplicatableModels: context.duplicatableModels,
193
+ nonDuplicatableModels: context.nonDuplicatableModels
194
+ })
195
+ };
196
+ } else {
197
+ return {
198
+ $$ref: localizedField.refId
199
+ };
200
+ }
201
+ }
202
+ }
203
+ break;
204
+ }
205
+ case 'list': {
206
+ const localizedField = getDocumentFieldForLocale(documentField, locale);
207
+ if (value) {
208
+ // do not try to merge provided list items with existing items
209
+ // because array items can not be matched by names like fields do.
210
+ break;
211
+ }
212
+ if (localizedField) {
213
+ return (localizedField.items ?? []).map((field) =>
214
+ mergeObjectWithDocumentField({
215
+ value: undefined,
216
+ documentField: field,
217
+ ...context
218
+ })
219
+ ).filter((value) => typeof value !== 'undefined') // if locale passed, it may return undefined for items localized to a different locale;
220
+ }
221
+ break;
222
+ }
223
+ default: {
224
+ const _exhaustiveCheck: never = documentField;
225
+ return _exhaustiveCheck;
226
+ }
227
+ }
228
+ // if the document value was not set and no merging occurred use the value
229
+ // that was provided to override the field in the duplicated document
230
+ return value;
231
+ }
232
+
233
+ function shouldDuplicate({
234
+ referenceField,
235
+ modelName,
236
+ seenReferences,
237
+ referenceBehavior,
238
+ nonDuplicatableModels,
239
+ duplicatableModels
240
+ }: {
241
+ referenceField: ContentStoreTypes.DocumentReferenceFieldNonLocalized & { isUnset?: false };
242
+ modelName: string;
243
+ seenReferences: string[];
244
+ referenceBehavior?: 'copyReference' | 'duplicateContents';
245
+ nonDuplicatableModels?: string[];
246
+ duplicatableModels?: string[];
247
+ }) {
248
+ return (
249
+ !seenReferences.includes(referenceField.refId) &&
250
+ modelName !== IMAGE_MODEL.name &&
251
+ ((referenceBehavior === 'duplicateContents' && !(nonDuplicatableModels ?? []).includes(modelName)) ||
252
+ (referenceBehavior === 'copyReference' && (duplicatableModels ?? []).includes(modelName)))
253
+ );
254
+ }
@@ -70,12 +70,26 @@ function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?:
70
70
  const localeProps = locale ? locales[locale] : undefined;
71
71
  return {
72
72
  ...base,
73
- ...(localeProps ?? { isUnset: true }),
73
+ ...(localeProps
74
+ ? {
75
+ ...localeProps,
76
+ fields: toLocalizedAPIFields(localeProps.fields, locale) as ContentStoreTypes.ImageFieldsAPI
77
+ }
78
+ : { isUnset: true }),
74
79
  ...localeFields(localized, locale)
75
80
  };
76
81
  }
77
82
  return {
78
- ...docField,
83
+ ...(!docField.isUnset
84
+ ? {
85
+ ...docField,
86
+ type: 'image',
87
+ fields: toLocalizedAPIFields(docField.fields, locale) as ContentStoreTypes.ImageFieldsAPI
88
+ }
89
+ : {
90
+ ...docField,
91
+ type: 'image'
92
+ }),
79
93
  ...localeFields(docField.localized, docField.locale)
80
94
  };
81
95
  case 'object':