@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.
Files changed (83) hide show
  1. package/dist/analyzer/analyze-schema-types.d.ts +1 -0
  2. package/dist/analyzer/analyze-schema-types.d.ts.map +1 -0
  3. package/dist/analyzer/analyzer-utils.d.ts +1 -0
  4. package/dist/analyzer/analyzer-utils.d.ts.map +1 -0
  5. package/dist/analyzer/cms-matcher.d.ts +1 -0
  6. package/dist/analyzer/cms-matcher.d.ts.map +1 -0
  7. package/dist/analyzer/file-browser.d.ts +1 -0
  8. package/dist/analyzer/file-browser.d.ts.map +1 -0
  9. package/dist/analyzer/schema-generator.d.ts +1 -0
  10. package/dist/analyzer/schema-generator.d.ts.map +1 -0
  11. package/dist/analyzer/site-analyzer.d.ts +1 -0
  12. package/dist/analyzer/site-analyzer.d.ts.map +1 -0
  13. package/dist/analyzer/ssg-matcher.d.ts +1 -0
  14. package/dist/analyzer/ssg-matcher.d.ts.map +1 -0
  15. package/dist/config/config-consts.d.ts +1 -1
  16. package/dist/config/config-consts.d.ts.map +1 -0
  17. package/dist/config/config-consts.js +0 -1
  18. package/dist/config/config-consts.js.map +1 -1
  19. package/dist/config/config-errors.d.ts +1 -0
  20. package/dist/config/config-errors.d.ts.map +1 -0
  21. package/dist/config/config-loader-esbuild.d.ts +1 -0
  22. package/dist/config/config-loader-esbuild.d.ts.map +1 -0
  23. package/dist/config/config-loader.d.ts +16 -1
  24. package/dist/config/config-loader.d.ts.map +1 -0
  25. package/dist/config/config-loader.js +15 -4
  26. package/dist/config/config-loader.js.map +1 -1
  27. package/dist/config/config-schema/style-field-schema.d.ts +1 -0
  28. package/dist/config/config-schema/style-field-schema.d.ts.map +1 -0
  29. package/dist/config/config-schema/style-field-schema.js +4 -4
  30. package/dist/config/config-schema/style-field-schema.js.map +1 -1
  31. package/dist/config/config-schema.d.ts +1 -0
  32. package/dist/config/config-schema.d.ts.map +1 -0
  33. package/dist/config/config-schema.js +12 -0
  34. package/dist/config/config-schema.js.map +1 -1
  35. package/dist/config/config-types.d.ts +16 -1
  36. package/dist/config/config-types.d.ts.map +1 -0
  37. package/dist/config/config-validator.d.ts +1 -0
  38. package/dist/config/config-validator.d.ts.map +1 -0
  39. package/dist/config/config-writer.d.ts +1 -0
  40. package/dist/config/config-writer.d.ts.map +1 -0
  41. package/dist/config/presets-loader.d.ts +1 -0
  42. package/dist/config/presets-loader.d.ts.map +1 -0
  43. package/dist/config/presets-loader.js +11 -6
  44. package/dist/config/presets-loader.js.map +1 -1
  45. package/dist/consts.d.ts +1 -0
  46. package/dist/consts.d.ts.map +1 -0
  47. package/dist/content/content-errors.d.ts +1 -0
  48. package/dist/content/content-errors.d.ts.map +1 -0
  49. package/dist/content/content-loader.d.ts +1 -0
  50. package/dist/content/content-loader.d.ts.map +1 -0
  51. package/dist/content/content-schema.d.ts +1 -0
  52. package/dist/content/content-schema.d.ts.map +1 -0
  53. package/dist/content/content-schema.js +44 -36
  54. package/dist/content/content-schema.js.map +1 -1
  55. package/dist/content/content-validator.d.ts +1 -0
  56. package/dist/content/content-validator.d.ts.map +1 -0
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +3 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/utils/index.d.ts +2 -1
  62. package/dist/utils/index.d.ts.map +1 -0
  63. package/dist/utils/model-extender.d.ts +1 -0
  64. package/dist/utils/model-extender.d.ts.map +1 -0
  65. package/dist/utils/model-iterators.d.ts +20 -0
  66. package/dist/utils/model-iterators.d.ts.map +1 -0
  67. package/dist/utils/model-iterators.js +99 -1
  68. package/dist/utils/model-iterators.js.map +1 -1
  69. package/dist/utils/model-matcher.d.ts +1 -0
  70. package/dist/utils/model-matcher.d.ts.map +1 -0
  71. package/dist/utils/model-utils.d.ts +1 -0
  72. package/dist/utils/model-utils.d.ts.map +1 -0
  73. package/package.json +3 -3
  74. package/src/config/config-consts.ts +0 -1
  75. package/src/config/config-loader.ts +32 -6
  76. package/src/config/config-schema/style-field-schema.ts +7 -4
  77. package/src/config/config-schema.ts +16 -0
  78. package/src/config/config-types.ts +28 -1
  79. package/src/config/presets-loader.ts +26 -10
  80. package/src/content/content-schema.ts +50 -44
  81. package/src/index.ts +1 -1
  82. package/src/utils/index.ts +1 -1
  83. 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: stylePropSchemaWithValidValues(STYLE_PROPS_VALUES.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: stylePropSchemaWithValidValues(STYLE_PROPS_VALUES.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.string().valid(...STYLE_PROPS_VALUES.fontWeight);
469
+ return Joi.number().valid(...values);
438
470
  }
439
- styleConfig = _.castArray(styleConfig);
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
- styleConfig = _.castArray(styleConfig);
476
- const validValues = _.reduce(
477
- styleConfig,
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[0]!;
496
- const end = parts[1]!;
497
- for (let i = start; i <= end; i += 5) {
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 Joi.valid(...[...validValues]);
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';
@@ -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,