@stackbit/sdk 0.2.38 → 0.2.39-alpha.0
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 +14 -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-types.d.ts +10 -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 +6 -5
- 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 +31 -6
- package/src/config/config-schema/style-field-schema.ts +7 -4
- package/src/config/config-types.ts +20 -1
- package/src/config/presets-loader.ts +21 -9
- 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
|
@@ -5,7 +5,12 @@ import semver from 'semver';
|
|
|
5
5
|
import _ from 'lodash';
|
|
6
6
|
|
|
7
7
|
import { ConfigValidationResult, validateConfig, validateContentModels } from './config-validator';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
ConfigError,
|
|
10
|
+
ConfigLoadError,
|
|
11
|
+
ConfigPresetsError,
|
|
12
|
+
ConfigValidationError
|
|
13
|
+
} from './config-errors';
|
|
9
14
|
import { loadStackbitConfigFromJs, LoadStackbitConfigResult, StopConfigWatch } from './config-loader-esbuild';
|
|
10
15
|
import {
|
|
11
16
|
assignLabelFieldIfNeeded,
|
|
@@ -60,7 +65,7 @@ export async function loadConfig({
|
|
|
60
65
|
stackbitConfigESBuildOutDir,
|
|
61
66
|
watchCallback
|
|
62
67
|
}: ConfigLoaderOptions): Promise<ConfigLoaderResult & StopConfigWatch> {
|
|
63
|
-
const rawConfigResult = await
|
|
68
|
+
const rawConfigResult = await loadConfigFromDir({
|
|
64
69
|
dirPath,
|
|
65
70
|
stackbitConfigESBuildOutDir,
|
|
66
71
|
watchCallback: watchCallback
|
|
@@ -96,14 +101,34 @@ async function processConfigLoaderResult({
|
|
|
96
101
|
|
|
97
102
|
const { models: externalModels, errors: externalModelsLoadErrors } = await loadModelsFromExternalSource(config, dirPath, modelsSource);
|
|
98
103
|
|
|
99
|
-
const
|
|
104
|
+
const extendedConfig = await extendConfig({ dirPath, config, externalModels });
|
|
100
105
|
|
|
101
|
-
|
|
106
|
+
return {
|
|
107
|
+
valid: extendedConfig.valid,
|
|
108
|
+
config: extendedConfig.config,
|
|
109
|
+
errors: [...configLoadErrors, ...externalModelsLoadErrors, ...extendedConfig.errors]
|
|
110
|
+
};
|
|
111
|
+
}
|
|
102
112
|
|
|
113
|
+
export async function extendConfig({
|
|
114
|
+
dirPath,
|
|
115
|
+
config,
|
|
116
|
+
externalModels
|
|
117
|
+
}: {
|
|
118
|
+
dirPath: string;
|
|
119
|
+
config: Record<string, any>;
|
|
120
|
+
externalModels?: Model[];
|
|
121
|
+
}): Promise<{
|
|
122
|
+
valid: boolean;
|
|
123
|
+
config: Config;
|
|
124
|
+
errors: (ConfigValidationError | ConfigPresetsError)[];
|
|
125
|
+
}> {
|
|
126
|
+
const normalizedResult = validateAndNormalizeConfig(config, externalModels);
|
|
127
|
+
const presetsResult = await loadPresets(dirPath, normalizedResult.config);
|
|
103
128
|
return {
|
|
104
129
|
valid: normalizedResult.valid,
|
|
105
130
|
config: presetsResult.config,
|
|
106
|
-
errors: [...
|
|
131
|
+
errors: [...normalizedResult.errors, ...presetsResult.errors]
|
|
107
132
|
};
|
|
108
133
|
}
|
|
109
134
|
|
|
@@ -139,7 +164,7 @@ export function validateAndNormalizeConfig(config: Record<string, any>, external
|
|
|
139
164
|
});
|
|
140
165
|
}
|
|
141
166
|
|
|
142
|
-
async function
|
|
167
|
+
export async function loadConfigFromDir({
|
|
143
168
|
dirPath,
|
|
144
169
|
stackbitConfigESBuildOutDir,
|
|
145
170
|
watchCallback
|
|
@@ -19,15 +19,15 @@ const styleSizeSchema = stylePropWithAll(
|
|
|
19
19
|
errors: { wrap: { label: false } }
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
const borderWidthSchema = stylePropWithAll(Joi.array().items(Joi.string().pattern(
|
|
22
|
+
const anyRangePattern = /^\d+(?::\d+(?::\d+)?)?$/;
|
|
23
|
+
const borderWidthSchema = stylePropWithAll(Joi.array().items(Joi.string().pattern(anyRangePattern)).single()).prefs({
|
|
24
24
|
messages: {
|
|
25
25
|
'string.pattern.base': 'Illegal definition "{{#value}}" of style field "{{#label}}". This field must match "borderWidth" style pattern'
|
|
26
26
|
},
|
|
27
27
|
errors: { wrap: { label: false } }
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
const fontWeightPattern = /^[1-8]00:[2-9]00
|
|
30
|
+
const fontWeightPattern = /^[1-8]00:[2-9]00(?::\d+)?$/;
|
|
31
31
|
const opacityPattern = /^[1-9]?[05]:(?:5|[1-9][05]|100)$/;
|
|
32
32
|
|
|
33
33
|
const styleColorSchema = arrayOf(
|
|
@@ -59,7 +59,10 @@ const stylePropsSchema = Joi.object({
|
|
|
59
59
|
label: Joi.string().required()
|
|
60
60
|
})
|
|
61
61
|
),
|
|
62
|
-
fontSize:
|
|
62
|
+
fontSize: stylePropWithAll(
|
|
63
|
+
Joi.string().pattern(sizePattern),
|
|
64
|
+
arrayOf(Joi.string().pattern(sizePattern), Joi.number().integer().min(0).multiple(1))
|
|
65
|
+
),
|
|
63
66
|
fontStyle: arrayOfStringsWithAll(...STYLE_PROPS_VALUES.fontStyle),
|
|
64
67
|
fontWeight: stylePropWithAll(
|
|
65
68
|
Joi.string().pattern(fontWeightPattern),
|
|
@@ -3,7 +3,7 @@ import { CMS_NAMES, FIELD_TYPES, SSG_NAMES, STYLE_PROPS } from './config-consts'
|
|
|
3
3
|
|
|
4
4
|
export interface Config extends BaseConfig {
|
|
5
5
|
models: Model[];
|
|
6
|
-
presets?: Record<string,
|
|
6
|
+
presets?: Record<string, Preset>;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface YamlConfig extends BaseConfig {
|
|
@@ -113,6 +113,13 @@ export interface ModelsSourceSanity {
|
|
|
113
113
|
[key: string]: any;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
export interface Preset {
|
|
117
|
+
label: string;
|
|
118
|
+
modelName: string;
|
|
119
|
+
thumbnail?: string;
|
|
120
|
+
data: Record<string, any>;
|
|
121
|
+
}
|
|
122
|
+
|
|
116
123
|
/*******************
|
|
117
124
|
*** Model Types ***
|
|
118
125
|
*******************/
|
|
@@ -213,6 +220,17 @@ export interface ImageModel {
|
|
|
213
220
|
|
|
214
221
|
export type Field = FieldSimple | FieldEnum | FieldImage | FieldNumber | FieldObject | FieldModel | FieldReference | FieldStyle | FieldList;
|
|
215
222
|
|
|
223
|
+
export type FieldSpecificProps =
|
|
224
|
+
| FieldSimpleProps
|
|
225
|
+
| FieldEnumProps
|
|
226
|
+
| FieldImageProps
|
|
227
|
+
| FieldNumberProps
|
|
228
|
+
| FieldObjectProps
|
|
229
|
+
| FieldModelProps
|
|
230
|
+
| FieldReferenceProps
|
|
231
|
+
| FieldStyleProps
|
|
232
|
+
| FieldListProps;
|
|
233
|
+
|
|
216
234
|
export type FieldSimple = FieldCommonProps & FieldSimpleProps;
|
|
217
235
|
export type FieldEnum = FieldCommonProps & FieldEnumProps;
|
|
218
236
|
export type FieldImage = FieldCommonProps & FieldImageProps;
|
|
@@ -238,6 +256,7 @@ export interface FieldCommonProps {
|
|
|
238
256
|
const?: unknown;
|
|
239
257
|
hidden?: boolean;
|
|
240
258
|
readOnly?: boolean;
|
|
259
|
+
localized?: boolean;
|
|
241
260
|
}
|
|
242
261
|
|
|
243
262
|
export type FieldType = typeof FIELD_TYPES[number];
|
|
@@ -3,7 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import fse from 'fs-extra';
|
|
4
4
|
import { parseFile, append } from '@stackbit/utils';
|
|
5
5
|
|
|
6
|
-
import { Config } from './config-types';
|
|
6
|
+
import { Config, Preset } from './config-types';
|
|
7
7
|
import { ConfigPresetsError } from './config-errors';
|
|
8
8
|
|
|
9
9
|
export interface PresetsLoaderResult {
|
|
@@ -11,6 +11,17 @@ export interface PresetsLoaderResult {
|
|
|
11
11
|
errors: ConfigPresetsError[];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
interface RawPresetData {
|
|
15
|
+
model: string;
|
|
16
|
+
presets: RawPreset[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface RawPreset {
|
|
20
|
+
label: string;
|
|
21
|
+
thumbnail?: string;
|
|
22
|
+
data: Record<string, any>;
|
|
23
|
+
}
|
|
24
|
+
|
|
14
25
|
export async function loadPresets(dirPath: string, config: Config): Promise<PresetsLoaderResult> {
|
|
15
26
|
const presetFiles = [];
|
|
16
27
|
let presetsRelDirs = ['.stackbit/presets', 'node_modules/@stackbit/components/presets'];
|
|
@@ -30,14 +41,14 @@ export async function loadPresets(dirPath: string, config: Config): Promise<Pres
|
|
|
30
41
|
presetFiles.push(...files);
|
|
31
42
|
}
|
|
32
43
|
|
|
33
|
-
const presets: Record<string,
|
|
34
|
-
const presetsIdsByModel: Record<string,
|
|
44
|
+
const presets: Record<string, Preset> = {};
|
|
45
|
+
const presetsIdsByModel: Record<string, string[]> = {};
|
|
35
46
|
const errors: ConfigPresetsError[] = [];
|
|
36
47
|
|
|
37
48
|
for (const presetFile of presetFiles) {
|
|
38
49
|
const presetsRelDir = path.dirname(presetFile);
|
|
39
50
|
const presetPath = path.join(dirPath, presetFile);
|
|
40
|
-
let presetData:
|
|
51
|
+
let presetData: RawPresetData;
|
|
41
52
|
try {
|
|
42
53
|
presetData = await parseFile(presetPath);
|
|
43
54
|
} catch (err: any) {
|
|
@@ -46,11 +57,12 @@ export async function loadPresets(dirPath: string, config: Config): Promise<Pres
|
|
|
46
57
|
}
|
|
47
58
|
_.forEach(_.get(presetData, 'presets', []), (preset, i) => {
|
|
48
59
|
const presetId = `${presetFile}:presets[${i}]`;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
const { thumbnail, ...rest } = preset;
|
|
61
|
+
presets[presetId] = {
|
|
62
|
+
...rest,
|
|
63
|
+
...(thumbnail ? { thumbnail: resolveThumbnailPath(thumbnail, presetsRelDir) } : null),
|
|
64
|
+
modelName: presetData.model
|
|
65
|
+
};
|
|
54
66
|
append(presetsIdsByModel, presetData.model, presetId);
|
|
55
67
|
});
|
|
56
68
|
}
|
|
@@ -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,
|