@stackbit/sdk 0.2.22 → 0.2.23
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.
- package/dist/config/config-consts.d.ts +1 -1
- package/dist/config/config-consts.js +2 -0
- package/dist/config/config-consts.js.map +1 -1
- package/dist/config/config-loader.d.ts +3 -2
- package/dist/config/config-loader.js +248 -71
- package/dist/config/config-loader.js.map +1 -1
- package/dist/config/config-schema.js +27 -3
- package/dist/config/config-schema.js.map +1 -1
- package/dist/config/config-types.d.ts +21 -4
- package/dist/config/config-writer.js +3 -0
- package/dist/config/config-writer.js.map +1 -1
- package/dist/config/presets-loader.js +17 -11
- package/dist/config/presets-loader.js.map +1 -1
- package/dist/content/content-loader.js +1 -1
- package/dist/content/content-schema.js +8 -0
- package/dist/content/content-schema.js.map +1 -1
- package/dist/utils/model-extender.js.map +1 -1
- package/dist/utils/model-iterators.d.ts +61 -1
- package/dist/utils/model-iterators.js +60 -11
- package/dist/utils/model-iterators.js.map +1 -1
- package/dist/utils/model-utils.d.ts +44 -3
- package/dist/utils/model-utils.js +93 -10
- package/dist/utils/model-utils.js.map +1 -1
- package/package.json +2 -2
- package/src/.DS_Store +0 -0
- package/src/config/config-consts.ts +2 -0
- package/src/config/config-loader.ts +272 -82
- package/src/config/config-schema.ts +34 -4
- package/src/config/config-types.ts +25 -4
- package/src/config/config-writer.ts +3 -0
- package/src/config/presets-loader.ts +18 -15
- package/src/content/content-loader.ts +2 -2
- package/src/content/content-schema.ts +9 -0
- package/src/utils/model-extender.ts +1 -1
- package/src/utils/model-iterators.ts +61 -13
- package/src/utils/model-utils.ts +91 -8
|
@@ -20,13 +20,14 @@ export async function loadPresets(dirPath: string, config: Config): Promise<Pres
|
|
|
20
20
|
if (!(await fse.pathExists(presetsDir))) {
|
|
21
21
|
continue;
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const files = (await fse.readdir(presetsDir))
|
|
24
|
+
.filter((fileName) => ['.json', '.yaml', '.yml'].includes(path.parse(fileName).ext))
|
|
25
|
+
.map((fileName) => path.join(presetsRelDir, fileName));
|
|
26
|
+
presetFiles.push(...files);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
const presets: any = {};
|
|
29
|
-
const
|
|
29
|
+
const presets: Record<string, any> = {};
|
|
30
|
+
const presetsIdsByModel: Record<string, any> = {};
|
|
30
31
|
const errors: ConfigPresetsError[] = [];
|
|
31
32
|
|
|
32
33
|
for (const presetFile of presetFiles) {
|
|
@@ -46,22 +47,24 @@ export async function loadPresets(dirPath: string, config: Config): Promise<Pres
|
|
|
46
47
|
preset.thumbnail = resolveThumbnailPath(preset.thumbnail, presetsRelDir);
|
|
47
48
|
}
|
|
48
49
|
_.set(preset, 'modelName', presetData.model);
|
|
49
|
-
append(
|
|
50
|
+
append(presetsIdsByModel, presetData.model, presetId);
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
// update
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
if (
|
|
57
|
-
model
|
|
54
|
+
// update models with presets IDs
|
|
55
|
+
const models = _.map(config.models, (model) => {
|
|
56
|
+
const presetIdsForModel = presetsIdsByModel[model.name];
|
|
57
|
+
if (!presetIdsForModel) {
|
|
58
|
+
return model;
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
config.presets = presets;
|
|
60
|
+
return { ...model, presets: presetIdsForModel };
|
|
61
|
+
});
|
|
62
62
|
|
|
63
63
|
return {
|
|
64
|
-
config,
|
|
64
|
+
config: Object.assign({}, config, {
|
|
65
|
+
models,
|
|
66
|
+
presets
|
|
67
|
+
}),
|
|
65
68
|
errors
|
|
66
69
|
};
|
|
67
70
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
isDataModel,
|
|
11
11
|
isPageModel,
|
|
12
12
|
getModelsByQuery,
|
|
13
|
-
|
|
13
|
+
getListFieldItems,
|
|
14
14
|
isListField,
|
|
15
15
|
isModelField,
|
|
16
16
|
isListDataModel,
|
|
@@ -480,7 +480,7 @@ function addMetadataRecursively({
|
|
|
480
480
|
} else if (_.isArray(value)) {
|
|
481
481
|
let fieldListItems: FieldListItems;
|
|
482
482
|
if (field && isListField(field)) {
|
|
483
|
-
fieldListItems =
|
|
483
|
+
fieldListItems = getListFieldItems(field);
|
|
484
484
|
} else if (model && isListDataModel(model)) {
|
|
485
485
|
fieldListItems = model.items;
|
|
486
486
|
} else {
|
|
@@ -106,6 +106,10 @@ function joiSchemaForModelFields(fields: Field[] | undefined, config: Config, fi
|
|
|
106
106
|
);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
function assertUnreachable(field: never): never {
|
|
110
|
+
throw new Error('Unhandled field.type case');
|
|
111
|
+
}
|
|
112
|
+
|
|
109
113
|
function joiSchemaForField(field: Field | FieldListItems, config: Config, fieldPath: FieldPath) {
|
|
110
114
|
let fieldSchema;
|
|
111
115
|
switch (field.type) {
|
|
@@ -148,6 +152,11 @@ function joiSchemaForField(field: Field | FieldListItems, config: Config, fieldP
|
|
|
148
152
|
case 'list':
|
|
149
153
|
fieldSchema = listFieldValueSchema(field, config, fieldPath);
|
|
150
154
|
break;
|
|
155
|
+
case 'json':
|
|
156
|
+
case 'richText':
|
|
157
|
+
return Joi.any().forbidden();
|
|
158
|
+
default:
|
|
159
|
+
assertUnreachable(field);
|
|
151
160
|
}
|
|
152
161
|
if ('const' in field) {
|
|
153
162
|
fieldSchema = fieldSchema.valid(field.const).invalid(null, '').required();
|
|
@@ -11,7 +11,7 @@ export function extendModelArray(models: Model[]): { models: Model[]; errors: Co
|
|
|
11
11
|
models,
|
|
12
12
|
(result: { models: Model[]; errors: ConfigValidationError[] }, model) => {
|
|
13
13
|
// YamlModel is the same as Model just without 'name' and '__metadata' properties
|
|
14
|
-
const { model: extendedModel, errors } = memorized(model, model.name, modelsByName);
|
|
14
|
+
const { model: extendedModel, errors } = memorized(model, model.name, modelsByName as ModelMap);
|
|
15
15
|
return {
|
|
16
16
|
models: result.models.concat(extendedModel),
|
|
17
17
|
errors: result.errors.concat(errors)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { Field, FieldListItems, FieldModelProps, Model } from '../config/config-types';
|
|
3
|
+
import { getListFieldItems, isListDataModel, isListField, isObjectListItems, isModelField, isObjectField, isModelListItems } from './model-utils';
|
|
4
|
+
import { DataModel, Field, FieldList, FieldListItems, FieldModelProps, FieldObjectProps, Model } from '../config/config-types';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* This function invokes the `iteratee` function for every field of the `model`.
|
|
@@ -39,24 +39,24 @@ import { Field, FieldListItems, FieldModelProps, Model } from '../config/config-
|
|
|
39
39
|
* @param model The model to iterate fields
|
|
40
40
|
* @param iteratee The callback function
|
|
41
41
|
*/
|
|
42
|
-
export function iterateModelFieldsRecursively(model: Model, iteratee: (field: Field,
|
|
43
|
-
function _iterateDeep({ fields,
|
|
44
|
-
|
|
42
|
+
export function iterateModelFieldsRecursively(model: Model, iteratee: (field: Field, modelKeyPath: string[]) => void) {
|
|
43
|
+
function _iterateDeep({ fields, modelKeyPath }: { fields: Field[]; modelKeyPath: string[] }) {
|
|
44
|
+
modelKeyPath = modelKeyPath.concat('fields');
|
|
45
45
|
_.forEach(fields, (field) => {
|
|
46
46
|
if (!field) {
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
-
const
|
|
50
|
-
iteratee(field,
|
|
49
|
+
const childModelKeyPath = modelKeyPath.concat(field.name);
|
|
50
|
+
iteratee(field, childModelKeyPath);
|
|
51
51
|
if (isObjectField(field)) {
|
|
52
52
|
_iterateDeep({
|
|
53
53
|
fields: field.fields,
|
|
54
|
-
|
|
54
|
+
modelKeyPath: childModelKeyPath
|
|
55
55
|
});
|
|
56
56
|
} else if (isListField(field) && field.items && isObjectListItems(field.items)) {
|
|
57
57
|
_iterateDeep({
|
|
58
58
|
fields: field.items?.fields,
|
|
59
|
-
|
|
59
|
+
modelKeyPath: childModelKeyPath.concat('items')
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
});
|
|
@@ -65,16 +65,64 @@ export function iterateModelFieldsRecursively(model: Model, iteratee: (field: Fi
|
|
|
65
65
|
if (model && isListDataModel(model) && model.items && isObjectListItems(model.items)) {
|
|
66
66
|
_iterateDeep({
|
|
67
67
|
fields: model.items?.fields,
|
|
68
|
-
|
|
68
|
+
modelKeyPath: ['items']
|
|
69
69
|
});
|
|
70
70
|
} else {
|
|
71
71
|
_iterateDeep({
|
|
72
72
|
fields: model?.fields || [],
|
|
73
|
-
|
|
73
|
+
modelKeyPath: []
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
export function mapModelFieldsRecursively(model: Model, iteratee: (field: Field, modelKeyPath: string[]) => Field) {
|
|
79
|
+
function _mapField(field: Field, modelKeyPath: string[]): Field {
|
|
80
|
+
if (!field) {
|
|
81
|
+
return field;
|
|
82
|
+
}
|
|
83
|
+
modelKeyPath = modelKeyPath.concat(field.name);
|
|
84
|
+
field = iteratee(field, modelKeyPath);
|
|
85
|
+
if (isObjectField(field)) {
|
|
86
|
+
return _mapObjectField(field, modelKeyPath);
|
|
87
|
+
} else if (isListField(field)) {
|
|
88
|
+
return _mapListField(field, modelKeyPath);
|
|
89
|
+
} else {
|
|
90
|
+
return field;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function _mapObjectField<T extends FieldObjectProps | Model>(field: T, modelKeyPath: string[]): T {
|
|
95
|
+
const fields = field.fields;
|
|
96
|
+
if (!fields) {
|
|
97
|
+
return field;
|
|
98
|
+
}
|
|
99
|
+
modelKeyPath = modelKeyPath.concat('fields');
|
|
100
|
+
return {
|
|
101
|
+
...field,
|
|
102
|
+
fields: _.map(fields, (field) => _mapField(field, modelKeyPath))
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function _mapListField<T extends FieldList | (DataModel & { isList: true })>(field: T, modelKeyPath: string[]): T {
|
|
107
|
+
const items = field.items;
|
|
108
|
+
if (!items || !isObjectListItems(items)) {
|
|
109
|
+
return field;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
...field,
|
|
113
|
+
items: _mapObjectField(items, modelKeyPath.concat('items'))
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!model) {
|
|
118
|
+
return model;
|
|
119
|
+
} else if (isListDataModel(model)) {
|
|
120
|
+
return _mapListField(model, []);
|
|
121
|
+
} else {
|
|
122
|
+
return _mapObjectField(model, []);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
78
126
|
export function iterateObjectFieldsWithModelRecursively(
|
|
79
127
|
value: any,
|
|
80
128
|
model: Model,
|
|
@@ -165,7 +213,7 @@ export function iterateObjectFieldsWithModelRecursively(
|
|
|
165
213
|
} else if (_.isArray(value)) {
|
|
166
214
|
let fieldListItems: FieldListItems | null = null;
|
|
167
215
|
if (field && isListField(field)) {
|
|
168
|
-
fieldListItems =
|
|
216
|
+
fieldListItems = getListFieldItems(field);
|
|
169
217
|
} else if (model && isListDataModel(model)) {
|
|
170
218
|
fieldListItems = model.items;
|
|
171
219
|
}
|
|
@@ -287,7 +335,7 @@ export function mapObjectFieldsWithModelRecursively(
|
|
|
287
335
|
} else if (_.isArray(value)) {
|
|
288
336
|
let fieldListItems: FieldListItems | null = null;
|
|
289
337
|
if (field && isListField(field)) {
|
|
290
|
-
fieldListItems =
|
|
338
|
+
fieldListItems = getListFieldItems(field);
|
|
291
339
|
} else if (model && isListDataModel(model)) {
|
|
292
340
|
fieldListItems = model.items;
|
|
293
341
|
}
|
package/src/utils/model-utils.ts
CHANGED
|
@@ -79,20 +79,20 @@ export function isEnumField(field: Field): field is FieldEnum {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
export function isListOfObjectsField(field: Field): field is FieldListObject {
|
|
82
|
-
return isListField(field) && isObjectListItems(
|
|
82
|
+
return isListField(field) && isObjectListItems(getListFieldItems(field));
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export function isListOfModelsField(field: Field): field is FieldListModel {
|
|
86
|
-
return isListField(field) && isModelListItems(
|
|
86
|
+
return isListField(field) && isModelListItems(getListFieldItems(field));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
export function isListOfReferencesField(field: Field): field is FieldListReference {
|
|
90
|
-
return isListField(field) && isReferenceListItems(
|
|
90
|
+
return isListField(field) && isReferenceListItems(getListFieldItems(field));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
export function isListOfCustomModelsField(field: Field, modelsByName?: Record<string, Model>): field is FieldList {
|
|
94
94
|
// custom model field types are deprecated
|
|
95
|
-
return isListField(field) && isCustomModelListItems(
|
|
95
|
+
return isListField(field) && isCustomModelListItems(getListFieldItems(field), modelsByName);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export function isListField(field: Field): field is FieldList {
|
|
@@ -122,7 +122,7 @@ export function isCustomModelListItems(items: FieldListItems, modelsByName?: Rec
|
|
|
122
122
|
* items field, the default field is string:
|
|
123
123
|
*
|
|
124
124
|
* @example
|
|
125
|
-
* listItemField =
|
|
125
|
+
* listItemField = getListFieldItems({
|
|
126
126
|
* type: 'list',
|
|
127
127
|
* name: '...',
|
|
128
128
|
* items: { type: 'object', fields: [] }
|
|
@@ -134,15 +134,36 @@ export function isCustomModelListItems(items: FieldListItems, modelsByName?: Rec
|
|
|
134
134
|
* }
|
|
135
135
|
*
|
|
136
136
|
* // list field without `items`
|
|
137
|
-
* listItemField =
|
|
137
|
+
* listItemField = getListFieldItems({ type: 'list', name: '...' }
|
|
138
138
|
* listItemField => { type: 'string' }
|
|
139
139
|
*
|
|
140
140
|
* @param {Object} field
|
|
141
141
|
* @return {Object}
|
|
142
142
|
*/
|
|
143
|
-
export function
|
|
143
|
+
export function getListFieldItems(field: FieldList): FieldListItems {
|
|
144
144
|
// items.type defaults to string
|
|
145
|
-
return
|
|
145
|
+
return Object.assign({ type: 'string' }, field.items);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function normalizeListField(field: FieldList): FieldList {
|
|
149
|
+
if (field.items?.type) {
|
|
150
|
+
return field;
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
...field,
|
|
154
|
+
items: {
|
|
155
|
+
type: 'string',
|
|
156
|
+
...(field.items ?? {})
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function normalizeListFieldInPlace(field: FieldList): FieldList {
|
|
162
|
+
// 'items.type' of list field default to 'string', set it explicitly
|
|
163
|
+
if (!_.has(field, 'items.type')) {
|
|
164
|
+
_.set(field, 'items.type', 'string');
|
|
165
|
+
}
|
|
166
|
+
return field;
|
|
146
167
|
}
|
|
147
168
|
|
|
148
169
|
export function assignLabelFieldIfNeeded(modelOrField: Model | FieldObjectProps) {
|
|
@@ -176,3 +197,65 @@ export function resolveLabelFieldForModel(modelOrField: Model | FieldObjectProps
|
|
|
176
197
|
}
|
|
177
198
|
return labelField || null;
|
|
178
199
|
}
|
|
200
|
+
|
|
201
|
+
export function getModelFieldForModelKeyPath(model: Model, modelKeyPath: string[]) {
|
|
202
|
+
function _getField(field: Field, modelKeyPath: string[]): Model | Field | FieldListItems | FieldObjectProps | null {
|
|
203
|
+
if (modelKeyPath.length === 0) {
|
|
204
|
+
return field;
|
|
205
|
+
} else if (isObjectField(field)) {
|
|
206
|
+
return _getObjectFields(field, modelKeyPath);
|
|
207
|
+
} else if (isListField(field)) {
|
|
208
|
+
return _getListItems(field, modelKeyPath);
|
|
209
|
+
} else {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function _getObjectFields<T extends FieldObjectProps | Model>(field: T, modelKeyPath: string[]): Model | Field | FieldListItems | FieldObjectProps | null {
|
|
215
|
+
if (modelKeyPath.length === 0) {
|
|
216
|
+
return field;
|
|
217
|
+
}
|
|
218
|
+
const key = modelKeyPath.shift();
|
|
219
|
+
if (key !== 'fields' || !field.fields) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
if (modelKeyPath.length === 0) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const fieldName = modelKeyPath.shift();
|
|
226
|
+
const childField = _.find(field.fields, (field) => field.name === fieldName);
|
|
227
|
+
if (!childField) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
return _getField(childField, modelKeyPath);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function _getListItems<T extends FieldList | (DataModel & { isList: true })>(
|
|
234
|
+
field: T,
|
|
235
|
+
modelKeyPath: string[]
|
|
236
|
+
): Model | Field | FieldListItems | FieldObjectProps | null {
|
|
237
|
+
if (modelKeyPath.length === 0) {
|
|
238
|
+
return field;
|
|
239
|
+
}
|
|
240
|
+
const key = modelKeyPath.shift();
|
|
241
|
+
if (key !== 'items' || !field.items) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
if (modelKeyPath.length === 0) {
|
|
245
|
+
return field.items;
|
|
246
|
+
}
|
|
247
|
+
if (!isObjectListItems(field.items)) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
return _getObjectFields(field.items, modelKeyPath);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
modelKeyPath = modelKeyPath.slice();
|
|
254
|
+
if (!model) {
|
|
255
|
+
return null;
|
|
256
|
+
} else if (isListDataModel(model)) {
|
|
257
|
+
return _getListItems(model, modelKeyPath);
|
|
258
|
+
} else {
|
|
259
|
+
return _getObjectFields(model, modelKeyPath);
|
|
260
|
+
}
|
|
261
|
+
}
|