@stackbit/sdk 0.2.37 → 0.2.39-alpha.1
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/analyzer/analyze-schema-types.d.ts +1 -0
- package/dist/analyzer/analyze-schema-types.d.ts.map +1 -0
- package/dist/analyzer/analyzer-utils.d.ts +1 -0
- package/dist/analyzer/analyzer-utils.d.ts.map +1 -0
- package/dist/analyzer/cms-matcher.d.ts +1 -0
- package/dist/analyzer/cms-matcher.d.ts.map +1 -0
- package/dist/analyzer/file-browser.d.ts +1 -0
- package/dist/analyzer/file-browser.d.ts.map +1 -0
- package/dist/analyzer/schema-generator.d.ts +1 -0
- package/dist/analyzer/schema-generator.d.ts.map +1 -0
- package/dist/analyzer/site-analyzer.d.ts +1 -0
- package/dist/analyzer/site-analyzer.d.ts.map +1 -0
- package/dist/analyzer/ssg-matcher.d.ts +1 -0
- package/dist/analyzer/ssg-matcher.d.ts.map +1 -0
- package/dist/config/config-consts.d.ts +1 -1
- package/dist/config/config-consts.d.ts.map +1 -0
- package/dist/config/config-consts.js +0 -1
- package/dist/config/config-consts.js.map +1 -1
- package/dist/config/config-errors.d.ts +1 -0
- package/dist/config/config-errors.d.ts.map +1 -0
- package/dist/config/config-loader-esbuild.d.ts +1 -0
- package/dist/config/config-loader-esbuild.d.ts.map +1 -0
- package/dist/config/config-loader.d.ts +16 -1
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +15 -4
- package/dist/config/config-loader.js.map +1 -1
- package/dist/config/config-schema/style-field-schema.d.ts +1 -0
- package/dist/config/config-schema/style-field-schema.d.ts.map +1 -0
- package/dist/config/config-schema/style-field-schema.js +4 -4
- package/dist/config/config-schema/style-field-schema.js.map +1 -1
- package/dist/config/config-schema.d.ts +1 -0
- package/dist/config/config-schema.d.ts.map +1 -0
- package/dist/config/config-schema.js +12 -0
- package/dist/config/config-schema.js.map +1 -1
- package/dist/config/config-types.d.ts +16 -1
- package/dist/config/config-types.d.ts.map +1 -0
- package/dist/config/config-validator.d.ts +1 -0
- package/dist/config/config-validator.d.ts.map +1 -0
- package/dist/config/config-writer.d.ts +1 -0
- package/dist/config/config-writer.d.ts.map +1 -0
- package/dist/config/presets-loader.d.ts +1 -0
- package/dist/config/presets-loader.d.ts.map +1 -0
- package/dist/config/presets-loader.js +11 -6
- package/dist/config/presets-loader.js.map +1 -1
- package/dist/consts.d.ts +1 -0
- package/dist/consts.d.ts.map +1 -0
- package/dist/content/content-errors.d.ts +1 -0
- package/dist/content/content-errors.d.ts.map +1 -0
- package/dist/content/content-loader.d.ts +1 -0
- package/dist/content/content-loader.d.ts.map +1 -0
- package/dist/content/content-schema.d.ts +1 -0
- package/dist/content/content-schema.d.ts.map +1 -0
- package/dist/content/content-schema.js +44 -36
- package/dist/content/content-schema.js.map +1 -1
- package/dist/content/content-validator.d.ts +1 -0
- package/dist/content/content-validator.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/model-extender.d.ts +1 -0
- package/dist/utils/model-extender.d.ts.map +1 -0
- package/dist/utils/model-iterators.d.ts +20 -0
- package/dist/utils/model-iterators.d.ts.map +1 -0
- package/dist/utils/model-iterators.js +99 -1
- package/dist/utils/model-iterators.js.map +1 -1
- package/dist/utils/model-matcher.d.ts +1 -0
- package/dist/utils/model-matcher.d.ts.map +1 -0
- package/dist/utils/model-utils.d.ts +1 -0
- package/dist/utils/model-utils.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/config/config-consts.ts +0 -1
- package/src/config/config-loader.ts +32 -6
- package/src/config/config-schema/style-field-schema.ts +7 -4
- package/src/config/config-schema.ts +16 -0
- package/src/config/config-types.ts +28 -1
- package/src/config/presets-loader.ts +26 -10
- package/src/content/content-schema.ts +50 -44
- package/src/index.ts +1 -1
- package/src/utils/index.ts +1 -1
- package/src/utils/model-iterators.ts +145 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import Joi from 'joi';
|
|
1
|
+
import Joi, { CustomHelpers, ErrorReport } from 'joi';
|
|
2
2
|
import _ from 'lodash';
|
|
3
3
|
import { append } from '@stackbit/utils';
|
|
4
|
-
|
|
5
4
|
import { STYLE_PROPS_VALUES } from '../config/config-consts';
|
|
6
5
|
import { isDataModel, isPageModel } from '../utils';
|
|
7
6
|
import {
|
|
@@ -119,11 +118,16 @@ function joiSchemaForField(field: Field | FieldListItems, config: Config, fieldP
|
|
|
119
118
|
case 'text':
|
|
120
119
|
case 'markdown':
|
|
121
120
|
case 'html':
|
|
122
|
-
case 'image':
|
|
123
121
|
case 'file':
|
|
124
122
|
case 'color':
|
|
125
123
|
fieldSchema = Joi.string().allow('', null);
|
|
126
124
|
break;
|
|
125
|
+
case 'image':
|
|
126
|
+
fieldSchema = Joi.alternatives([
|
|
127
|
+
Joi.string().allow('', null),
|
|
128
|
+
Joi.object()
|
|
129
|
+
]);
|
|
130
|
+
break;
|
|
127
131
|
case 'boolean':
|
|
128
132
|
fieldSchema = Joi.boolean();
|
|
129
133
|
break;
|
|
@@ -298,12 +302,12 @@ const StylePropContentSchemas: Record<StyleProps, (styleConfig: any) => Joi.Sche
|
|
|
298
302
|
width: stylePropSchemaWithValidValues(STYLE_PROPS_VALUES.width),
|
|
299
303
|
height: stylePropSchemaWithValidValues(STYLE_PROPS_VALUES.height),
|
|
300
304
|
fontFamily: stylePropObjectValueSchema,
|
|
301
|
-
fontSize:
|
|
305
|
+
fontSize: stylePropFontSizeSchema,
|
|
302
306
|
fontStyle: stylePropSchemaWithValidValues(STYLE_PROPS_VALUES.fontStyle),
|
|
303
307
|
fontWeight: stylePropFontWeightSchema,
|
|
304
308
|
textAlign: stylePropSchemaWithValidValues(STYLE_PROPS_VALUES.textAlign),
|
|
305
309
|
textColor: stylePropObjectValueSchema,
|
|
306
|
-
textDecoration:
|
|
310
|
+
textDecoration: stylePropTextDecorationSchema,
|
|
307
311
|
backgroundColor: stylePropObjectValueSchema,
|
|
308
312
|
backgroundPosition: stylePropSchemaWithValidValues(STYLE_PROPS_VALUES.nineRegions),
|
|
309
313
|
backgroundSize: stylePropSchemaWithValidValues(STYLE_PROPS_VALUES.backgroundSize),
|
|
@@ -327,6 +331,26 @@ function stylePropSchemaWithValidValues(validValues: any[]) {
|
|
|
327
331
|
};
|
|
328
332
|
}
|
|
329
333
|
|
|
334
|
+
const textDecorationWrongValue = 'textDecoration.wrong.value';
|
|
335
|
+
|
|
336
|
+
function stylePropTextDecorationSchema(styleConfig: any) {
|
|
337
|
+
const values = styleConfig === '*' ? STYLE_PROPS_VALUES.textDecoration : styleConfig;
|
|
338
|
+
return Joi.string().custom((value: string, { error, errorsArray }: CustomHelpers & { errorsArray?: () => ErrorReport[] }) => {
|
|
339
|
+
const parts = value.split?.(' ');
|
|
340
|
+
const errors = errorsArray!();
|
|
341
|
+
parts.forEach((part) => {
|
|
342
|
+
if (!values.includes(part)) {
|
|
343
|
+
errors.push(error(textDecorationWrongValue));
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
return errors && errors.length ? errors : value;
|
|
347
|
+
}).prefs({
|
|
348
|
+
messages: {
|
|
349
|
+
[textDecorationWrongValue]: `{{#label}} must be a space separated string values from [${values.join(', ')}]`
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
330
354
|
function stylePropSizeSchema(styleConfig: any) {
|
|
331
355
|
if (styleConfig === '*') {
|
|
332
356
|
return Joi.object({
|
|
@@ -432,49 +456,31 @@ function stylePropObjectValueSchema(styleConfig: any) {
|
|
|
432
456
|
return Joi.valid(..._.map(styleConfig, (object) => _.get(object, 'value')));
|
|
433
457
|
}
|
|
434
458
|
|
|
459
|
+
function stylePropFontSizeSchema(styleConfig: any) {
|
|
460
|
+
if (styleConfig === '*') {
|
|
461
|
+
return Joi.number().integer().min(0).max(100).multiple(1);
|
|
462
|
+
}
|
|
463
|
+
return Joi.number().valid(...parseRange(styleConfig));
|
|
464
|
+
}
|
|
465
|
+
|
|
435
466
|
function stylePropFontWeightSchema(styleConfig: any) {
|
|
467
|
+
const values = STYLE_PROPS_VALUES.fontWeight.map((valueStr) => Number(valueStr));
|
|
436
468
|
if (styleConfig === '*') {
|
|
437
|
-
return Joi.
|
|
469
|
+
return Joi.number().valid(...values);
|
|
438
470
|
}
|
|
439
|
-
|
|
440
|
-
const validValues = _.reduce(
|
|
441
|
-
styleConfig,
|
|
442
|
-
(validValues, value) => {
|
|
443
|
-
if (_.isNumber(value)) {
|
|
444
|
-
return validValues.add(String(value));
|
|
445
|
-
}
|
|
446
|
-
if (!_.isString(value)) {
|
|
447
|
-
return validValues;
|
|
448
|
-
}
|
|
449
|
-
if (_.isEmpty(value)) {
|
|
450
|
-
return validValues;
|
|
451
|
-
}
|
|
452
|
-
const parts = value.split(':').map((value) => Number(value));
|
|
453
|
-
if (_.some(parts, _.isNaN)) {
|
|
454
|
-
return validValues;
|
|
455
|
-
}
|
|
456
|
-
if (parts.length === 1) {
|
|
457
|
-
return validValues.add(String(parts[0]!));
|
|
458
|
-
}
|
|
459
|
-
const start = parts[0]!;
|
|
460
|
-
const end = parts[1]!;
|
|
461
|
-
for (let i = start; i <= end; i += 100) {
|
|
462
|
-
validValues.add(String(i));
|
|
463
|
-
}
|
|
464
|
-
return validValues;
|
|
465
|
-
},
|
|
466
|
-
new Set<string>()
|
|
467
|
-
);
|
|
468
|
-
return Joi.valid(...[...validValues]);
|
|
471
|
+
return Joi.number().valid(...parseRange(styleConfig, { defaultStep: 100 }));
|
|
469
472
|
}
|
|
470
473
|
|
|
471
474
|
function stylePropOpacitySchema(styleConfig: any) {
|
|
472
475
|
if (styleConfig === '*') {
|
|
473
476
|
return Joi.number().integer().min(0).max(100).multiple(5);
|
|
474
477
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
+
return Joi.number().valid(...parseRange(styleConfig, { userDefinedStep: false, defaultStep: 5 }));
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function parseRange(styleConfig: any, { userDefinedStep = true, defaultStep = 1 } = {}) {
|
|
482
|
+
const values = _.reduce(
|
|
483
|
+
_.castArray(styleConfig),
|
|
478
484
|
(validValues, value) => {
|
|
479
485
|
if (_.isNumber(value)) {
|
|
480
486
|
return validValues.add(value);
|
|
@@ -492,14 +498,14 @@ function stylePropOpacitySchema(styleConfig: any) {
|
|
|
492
498
|
if (parts.length === 1) {
|
|
493
499
|
return validValues.add(parts[0]!);
|
|
494
500
|
}
|
|
495
|
-
const start = parts
|
|
496
|
-
const
|
|
497
|
-
for (let i = start
|
|
501
|
+
const [start, end, userStep = defaultStep] = parts;
|
|
502
|
+
const step = userDefinedStep ? userStep : defaultStep;
|
|
503
|
+
for (let i = start!; i <= end!; i += step) {
|
|
498
504
|
validValues.add(i);
|
|
499
505
|
}
|
|
500
506
|
return validValues;
|
|
501
507
|
},
|
|
502
508
|
new Set<number>()
|
|
503
509
|
);
|
|
504
|
-
return
|
|
505
|
-
}
|
|
510
|
+
return Array.from(values);
|
|
511
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ export * from './content/content-errors';
|
|
|
5
5
|
export * from './config/config-errors';
|
|
6
6
|
export * from './analyzer/file-browser';
|
|
7
7
|
export * from './utils';
|
|
8
|
-
export { loadConfig, ConfigLoaderOptions, ConfigLoaderResult } from './config/config-loader';
|
|
8
|
+
export { loadConfig, loadConfigFromDir, extendConfig, ConfigLoaderOptions, ConfigLoaderResult } from './config/config-loader';
|
|
9
9
|
export { writeConfig, WriteConfigOptions, convertToYamlConfig } from './config/config-writer';
|
|
10
10
|
export { loadContent, ContentItem, ContentLoaderOptions, ContentLoaderResult } from './content/content-loader';
|
|
11
11
|
export { matchSSG, SSGMatcherOptions, SSGMatchResult } from './analyzer/ssg-matcher';
|
package/src/utils/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ type StricterUnionMember<T, Union, Keys extends string = UnionKeys<Union> extend
|
|
|
9
9
|
|
|
10
10
|
export type StricterUnion<Union, Union2 = Union> = Union2 extends any ? StricterUnionMember<Union2, Union> : never;
|
|
11
11
|
|
|
12
|
-
export type Logger = Pick<Console, 'log' | 'info' | 'warn' | 'error' | 'group' | 'groupEnd'>;
|
|
12
|
+
export type Logger = Pick<Console, 'log' | 'debug' | 'info' | 'warn' | 'error' | 'group' | 'groupEnd'>;
|
|
13
13
|
|
|
14
14
|
export * from './model-utils';
|
|
15
15
|
export * from './model-matcher';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
|
|
3
|
+
import { mapPromise, mapValuesPromise } from '@stackbit/utils';
|
|
3
4
|
import { getListFieldItems, isListDataModel, isListField, isObjectListItems, isModelField, isObjectField, isModelListItems } from './model-utils';
|
|
4
5
|
import { DataModel, Field, FieldList, FieldListItems, FieldModelProps, FieldObjectProps, Model } from '../config/config-types';
|
|
5
6
|
|
|
@@ -366,6 +367,150 @@ export function mapObjectFieldsWithModelRecursively(
|
|
|
366
367
|
});
|
|
367
368
|
}
|
|
368
369
|
|
|
370
|
+
export async function asyncMapObjectFieldsWithModelRecursively({
|
|
371
|
+
value,
|
|
372
|
+
model,
|
|
373
|
+
modelsByName,
|
|
374
|
+
iteratee,
|
|
375
|
+
pageLayoutKey = 'layout',
|
|
376
|
+
objectTypeKey = 'type',
|
|
377
|
+
valueId
|
|
378
|
+
}: {
|
|
379
|
+
value: any;
|
|
380
|
+
model: Model;
|
|
381
|
+
modelsByName: Record<string, Model>;
|
|
382
|
+
iteratee: (options: {
|
|
383
|
+
value: any;
|
|
384
|
+
model: Model | null;
|
|
385
|
+
field: Field | null;
|
|
386
|
+
fieldListItem: FieldListItems | null;
|
|
387
|
+
error: string | null;
|
|
388
|
+
valueKeyPath: (string | number)[];
|
|
389
|
+
modelKeyPath: string[];
|
|
390
|
+
objectStack: any[];
|
|
391
|
+
skipNested: () => void;
|
|
392
|
+
}) => Promise<any>;
|
|
393
|
+
pageLayoutKey?: string;
|
|
394
|
+
objectTypeKey?: string;
|
|
395
|
+
valueId?: string;
|
|
396
|
+
}): Promise<any> {
|
|
397
|
+
async function _mapDeep({
|
|
398
|
+
value,
|
|
399
|
+
model,
|
|
400
|
+
field,
|
|
401
|
+
fieldListItem,
|
|
402
|
+
valueKeyPath,
|
|
403
|
+
modelKeyPath,
|
|
404
|
+
objectStack
|
|
405
|
+
}: {
|
|
406
|
+
value: any;
|
|
407
|
+
model: Model | null;
|
|
408
|
+
field: Field | null;
|
|
409
|
+
fieldListItem: FieldListItems | null;
|
|
410
|
+
valueKeyPath: (string | number)[];
|
|
411
|
+
modelKeyPath: string[];
|
|
412
|
+
objectStack: any[];
|
|
413
|
+
}) {
|
|
414
|
+
let error: string | null = null;
|
|
415
|
+
let modelField: FieldModelProps | null = null;
|
|
416
|
+
|
|
417
|
+
if (!model && !field && !fieldListItem) {
|
|
418
|
+
error = `could not match model/field ${modelKeyPath.join('.')} to content at ${valueKeyPath.join('.')}`;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (field && isModelField(field)) {
|
|
422
|
+
modelField = field;
|
|
423
|
+
} else if (fieldListItem && isModelListItems(fieldListItem)) {
|
|
424
|
+
modelField = fieldListItem;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (modelField) {
|
|
428
|
+
const modelResult = getModelOfObject({
|
|
429
|
+
object: value,
|
|
430
|
+
field: modelField,
|
|
431
|
+
modelsByName,
|
|
432
|
+
pageLayoutKey,
|
|
433
|
+
objectTypeKey,
|
|
434
|
+
valueKeyPath,
|
|
435
|
+
modelKeyPath
|
|
436
|
+
});
|
|
437
|
+
if ('error' in modelResult) {
|
|
438
|
+
error = modelResult.error;
|
|
439
|
+
} else {
|
|
440
|
+
model = modelResult.model;
|
|
441
|
+
}
|
|
442
|
+
field = null;
|
|
443
|
+
fieldListItem = null;
|
|
444
|
+
modelKeyPath = model ? [model.name] : [];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
let shouldSkipNested = false;
|
|
448
|
+
const skipNested = () => {
|
|
449
|
+
shouldSkipNested = true;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const res = await iteratee({ value, model, field, fieldListItem, error, valueKeyPath, modelKeyPath, objectStack, skipNested });
|
|
453
|
+
if (!_.isUndefined(res)) {
|
|
454
|
+
value = res;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (shouldSkipNested) {
|
|
458
|
+
return value;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (_.isPlainObject(value)) {
|
|
462
|
+
// if fields will not be resolved or the object will have a key that
|
|
463
|
+
// doesn't exist among fields, the nested calls to _iterateDeep will
|
|
464
|
+
// include an error.
|
|
465
|
+
const fields = getFieldsOfModelOrField(model, field, fieldListItem);
|
|
466
|
+
const fieldsByName = _.keyBy(fields, 'name');
|
|
467
|
+
modelKeyPath = _.concat(modelKeyPath, 'fields');
|
|
468
|
+
value = await mapValuesPromise(value, (val, key) => {
|
|
469
|
+
const field = _.get(fieldsByName, key, null);
|
|
470
|
+
return _mapDeep({
|
|
471
|
+
value: val,
|
|
472
|
+
model: null,
|
|
473
|
+
field: field,
|
|
474
|
+
fieldListItem: null,
|
|
475
|
+
valueKeyPath: _.concat(valueKeyPath, key),
|
|
476
|
+
modelKeyPath: _.concat(modelKeyPath, key),
|
|
477
|
+
objectStack: _.concat(objectStack, value)
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
} else if (_.isArray(value)) {
|
|
481
|
+
let fieldListItems: FieldListItems | null = null;
|
|
482
|
+
if (field && isListField(field)) {
|
|
483
|
+
fieldListItems = getListFieldItems(field);
|
|
484
|
+
} else if (model && isListDataModel(model)) {
|
|
485
|
+
fieldListItems = model.items;
|
|
486
|
+
}
|
|
487
|
+
value = await mapPromise(value, (val, idx) => {
|
|
488
|
+
return _mapDeep({
|
|
489
|
+
value: val,
|
|
490
|
+
model: null,
|
|
491
|
+
field: null,
|
|
492
|
+
fieldListItem: fieldListItems,
|
|
493
|
+
valueKeyPath: _.concat(valueKeyPath, idx),
|
|
494
|
+
modelKeyPath: _.concat(modelKeyPath, 'items'),
|
|
495
|
+
objectStack: _.concat(objectStack, value)
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return value;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return _mapDeep({
|
|
504
|
+
value: value,
|
|
505
|
+
model: model,
|
|
506
|
+
field: null,
|
|
507
|
+
fieldListItem: null,
|
|
508
|
+
valueKeyPath: valueId ? [valueId] : [],
|
|
509
|
+
modelKeyPath: [model.name],
|
|
510
|
+
objectStack: []
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
369
514
|
export function getModelOfObject({
|
|
370
515
|
object,
|
|
371
516
|
field,
|