@stackbit/sdk 0.2.30 → 0.2.33
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 +3 -2
- package/dist/analyzer/analyze-schema-types.js.map +1 -1
- package/dist/analyzer/schema-generator.js +4 -1
- package/dist/analyzer/schema-generator.js.map +1 -1
- package/dist/config/config-errors.js +4 -1
- package/dist/config/config-errors.js.map +1 -1
- package/dist/config/config-loader-esbuild.d.ts +19 -0
- package/dist/config/config-loader-esbuild.js +135 -0
- package/dist/config/config-loader-esbuild.js.map +1 -0
- package/dist/config/config-loader.d.ts +5 -2
- package/dist/config/config-loader.js +75 -22
- package/dist/config/config-loader.js.map +1 -1
- package/dist/config/config-schema.js +7 -1
- package/dist/config/config-schema.js.map +1 -1
- package/dist/config/config-types.d.ts +10 -3
- package/dist/utils/model-iterators.d.ts +5 -0
- package/dist/utils/model-utils.d.ts +6 -1
- package/package.json +3 -2
- package/src/analyzer/analyze-schema-types.ts +3 -0
- package/src/analyzer/schema-generator.ts +4 -1
- package/src/config/config-errors.ts +4 -1
- package/src/config/config-loader-esbuild.ts +137 -0
- package/src/config/config-loader.ts +105 -24
- package/src/config/config-schema.ts +8 -1
- package/src/config/config-types.ts +11 -3
|
@@ -6,6 +6,7 @@ import _ from 'lodash';
|
|
|
6
6
|
|
|
7
7
|
import { ConfigValidationResult, validateConfig, validateContentModels } from './config-validator';
|
|
8
8
|
import { ConfigError, ConfigLoadError, ConfigValidationError } from './config-errors';
|
|
9
|
+
import { loadStackbitConfigFromJs, LoadStackbitConfigResult, StopConfigWatch } from './config-loader-esbuild';
|
|
9
10
|
import {
|
|
10
11
|
assignLabelFieldIfNeeded,
|
|
11
12
|
extendModelMap,
|
|
@@ -25,13 +26,15 @@ import {
|
|
|
25
26
|
normalizeListFieldInPlace,
|
|
26
27
|
getModelFieldForModelKeyPath
|
|
27
28
|
} from '../utils';
|
|
28
|
-
import { append, prepend, omitByNil, parseFile, readDirRecursively, reducePromise, rename } from '@stackbit/utils';
|
|
29
|
+
import { append, prepend, omitByNil, parseFile, readDirRecursively, reducePromise, rename, getFirstExistingFile } from '@stackbit/utils';
|
|
29
30
|
import { Config, DataModel, FieldEnum, FieldModel, FieldObjectProps, Model, ModelsSource, PageModel, YamlModel } from './config-types';
|
|
30
31
|
import { loadPresets } from './presets-loader';
|
|
31
32
|
|
|
32
33
|
export interface ConfigLoaderOptions {
|
|
33
34
|
dirPath: string;
|
|
34
35
|
modelsSource?: ModelsSource;
|
|
36
|
+
stackbitConfigESBuildOutDir?: string;
|
|
37
|
+
watchCallback?: (result: ConfigLoaderResult) => void;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
export interface ConfigLoaderResult {
|
|
@@ -46,13 +49,42 @@ export interface NormalizedValidationResult {
|
|
|
46
49
|
errors: ConfigValidationError[];
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
export interface
|
|
52
|
+
export interface RawConfigLoaderResult {
|
|
50
53
|
config?: Record<string, any>;
|
|
51
54
|
errors: ConfigLoadError[];
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
export async function loadConfig({
|
|
55
|
-
|
|
57
|
+
export async function loadConfig({
|
|
58
|
+
dirPath,
|
|
59
|
+
modelsSource,
|
|
60
|
+
stackbitConfigESBuildOutDir,
|
|
61
|
+
watchCallback
|
|
62
|
+
}: ConfigLoaderOptions): Promise<ConfigLoaderResult & StopConfigWatch> {
|
|
63
|
+
const rawConfigResult = await loadStackbitConfigFromDir({
|
|
64
|
+
dirPath,
|
|
65
|
+
stackbitConfigESBuildOutDir,
|
|
66
|
+
watchCallback: watchCallback
|
|
67
|
+
? async (rawConfigResult: RawConfigLoaderResult) => {
|
|
68
|
+
const configLoaderResult = await processConfigLoaderResult({ rawConfigResult, dirPath, modelsSource });
|
|
69
|
+
watchCallback(configLoaderResult);
|
|
70
|
+
}
|
|
71
|
+
: undefined
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const configLoaderResult = await processConfigLoaderResult({ rawConfigResult, dirPath, modelsSource });
|
|
75
|
+
return Object.assign(configLoaderResult, { stop: rawConfigResult.stop });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function processConfigLoaderResult({
|
|
79
|
+
rawConfigResult,
|
|
80
|
+
dirPath,
|
|
81
|
+
modelsSource
|
|
82
|
+
}: {
|
|
83
|
+
rawConfigResult: RawConfigLoaderResult;
|
|
84
|
+
dirPath: string;
|
|
85
|
+
modelsSource?: ModelsSource;
|
|
86
|
+
}): Promise<ConfigLoaderResult> {
|
|
87
|
+
const { config, errors: configLoadErrors } = rawConfigResult;
|
|
56
88
|
|
|
57
89
|
if (!config) {
|
|
58
90
|
return {
|
|
@@ -107,41 +139,90 @@ export function validateAndNormalizeConfig(config: Record<string, any>, external
|
|
|
107
139
|
});
|
|
108
140
|
}
|
|
109
141
|
|
|
110
|
-
async function
|
|
142
|
+
async function loadStackbitConfigFromDir({
|
|
143
|
+
dirPath,
|
|
144
|
+
stackbitConfigESBuildOutDir,
|
|
145
|
+
watchCallback
|
|
146
|
+
}: {
|
|
147
|
+
dirPath: string;
|
|
148
|
+
stackbitConfigESBuildOutDir?: string;
|
|
149
|
+
watchCallback?: (rawConfigResult: RawConfigLoaderResult) => void;
|
|
150
|
+
}): Promise<RawConfigLoaderResult & StopConfigWatch> {
|
|
151
|
+
// try to load stackbit config from YAML files
|
|
111
152
|
try {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
153
|
+
const stackbitYamlPath = path.join(dirPath, 'stackbit.yaml');
|
|
154
|
+
const stackbitYamlExists = await fse.pathExists(stackbitYamlPath);
|
|
155
|
+
if (stackbitYamlExists) {
|
|
156
|
+
const stackbitYamlResult = await loadConfigFromStackbitYaml(stackbitYamlPath);
|
|
157
|
+
if (stackbitYamlResult.error) {
|
|
158
|
+
return { errors: [stackbitYamlResult.error] };
|
|
159
|
+
}
|
|
116
160
|
|
|
117
|
-
|
|
161
|
+
const { models: modelsFromFiles, errors: fileModelsErrors } = await loadModelsFromFiles(dirPath, stackbitYamlResult.config);
|
|
118
162
|
|
|
119
|
-
|
|
163
|
+
const mergedModels = mergeConfigModelsWithModelsFromFiles(stackbitYamlResult.config.models ?? {}, modelsFromFiles);
|
|
120
164
|
|
|
165
|
+
return {
|
|
166
|
+
config: {
|
|
167
|
+
...stackbitYamlResult.config,
|
|
168
|
+
models: mergedModels
|
|
169
|
+
},
|
|
170
|
+
errors: fileModelsErrors
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
} catch (error: any) {
|
|
121
174
|
return {
|
|
122
|
-
|
|
123
|
-
...stackbitYamlResult.config,
|
|
124
|
-
models: mergedModels
|
|
125
|
-
},
|
|
126
|
-
errors: fileModelsErrors
|
|
175
|
+
errors: [new ConfigLoadError(`Error loading Stackbit configuration: ${error.message}`, { originalError: error })]
|
|
127
176
|
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function wrapResult(result: LoadStackbitConfigResult) {
|
|
180
|
+
if (result.error) {
|
|
181
|
+
return {
|
|
182
|
+
errors: [result.error]
|
|
183
|
+
};
|
|
184
|
+
} else {
|
|
185
|
+
return {
|
|
186
|
+
config: result.config,
|
|
187
|
+
errors: []
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// try to load stackbit config from JavaScript files
|
|
193
|
+
try {
|
|
194
|
+
const configFilePath = await getFirstExistingFile(['stackbit.config.js', 'stackbit.config.cjs', 'stackbit.config.mjs', 'stackbit.config.ts'], dirPath);
|
|
195
|
+
if (configFilePath) {
|
|
196
|
+
const configResult = await loadStackbitConfigFromJs({
|
|
197
|
+
configPath: configFilePath,
|
|
198
|
+
outDir: stackbitConfigESBuildOutDir,
|
|
199
|
+
watch: !!watchCallback,
|
|
200
|
+
callback: watchCallback
|
|
201
|
+
? (result: LoadStackbitConfigResult) => {
|
|
202
|
+
watchCallback(wrapResult(result));
|
|
203
|
+
}
|
|
204
|
+
: undefined
|
|
205
|
+
});
|
|
206
|
+
return Object.assign(wrapResult(configResult), { stop: configResult.stop });
|
|
207
|
+
}
|
|
128
208
|
} catch (error: any) {
|
|
129
209
|
return {
|
|
130
210
|
errors: [new ConfigLoadError(`Error loading Stackbit configuration: ${error.message}`, { originalError: error })]
|
|
131
211
|
};
|
|
132
212
|
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
errors: [
|
|
216
|
+
new ConfigLoadError(
|
|
217
|
+
'stackbit.yaml or stackbit.config.js was not found, please refer Stackbit documentation: https://www.stackbit.com/docs/stackbit-yaml/'
|
|
218
|
+
)
|
|
219
|
+
]
|
|
220
|
+
};
|
|
133
221
|
}
|
|
134
222
|
|
|
135
223
|
type StackbitYamlConfigResult = { config: Record<string, any>; error?: never } | { config?: never; error: ConfigLoadError };
|
|
136
224
|
|
|
137
|
-
async function loadConfigFromStackbitYaml(
|
|
138
|
-
const stackbitYamlPath = path.join(dirPath, 'stackbit.yaml');
|
|
139
|
-
const stackbitYamlExists = await fse.pathExists(stackbitYamlPath);
|
|
140
|
-
if (!stackbitYamlExists) {
|
|
141
|
-
return {
|
|
142
|
-
error: new ConfigLoadError('stackbit.yaml was not found, please refer Stackbit documentation: https://www.stackbit.com/docs/stackbit-yaml/')
|
|
143
|
-
};
|
|
144
|
-
}
|
|
225
|
+
async function loadConfigFromStackbitYaml(stackbitYamlPath: string): Promise<StackbitYamlConfigResult> {
|
|
145
226
|
const stackbitYaml = await fse.readFile(stackbitYamlPath);
|
|
146
227
|
const config = yaml.load(stackbitYaml.toString('utf8'), { schema: yaml.JSON_SCHEMA });
|
|
147
228
|
if (!config || typeof config !== 'object') {
|
|
@@ -300,7 +300,8 @@ const fieldGroupsSchema = Joi.array()
|
|
|
300
300
|
.items(
|
|
301
301
|
Joi.object<FieldGroupItem>({
|
|
302
302
|
name: Joi.string().required(),
|
|
303
|
-
label: Joi.string().required()
|
|
303
|
+
label: Joi.string().required(),
|
|
304
|
+
icon: Joi.string().optional()
|
|
304
305
|
})
|
|
305
306
|
)
|
|
306
307
|
.unique('name')
|
|
@@ -357,6 +358,10 @@ const enumFieldBaseOptionSchema = Joi.object({
|
|
|
357
358
|
value: Joi.alternatives().try(Joi.string(), Joi.number()).required()
|
|
358
359
|
});
|
|
359
360
|
|
|
361
|
+
const imageFieldPartialSchema = Joi.object({
|
|
362
|
+
source: Joi.string()
|
|
363
|
+
});
|
|
364
|
+
|
|
360
365
|
const enumFieldPartialSchema = Joi.object({
|
|
361
366
|
type: Joi.string().valid('enum').required(),
|
|
362
367
|
controlType: Joi.string().valid('dropdown', 'button-group', 'thumbnails', 'palette'),
|
|
@@ -430,6 +435,7 @@ const listItemsSchema = Joi.object({
|
|
|
430
435
|
switch: [
|
|
431
436
|
{ is: 'number', then: numberFieldPartialSchema },
|
|
432
437
|
{ is: 'enum', then: enumFieldPartialSchema },
|
|
438
|
+
{ is: 'image', then: imageFieldPartialSchema },
|
|
433
439
|
{ is: 'object', then: objectFieldPartialSchema },
|
|
434
440
|
{ is: 'model', then: modelFieldPartialSchema },
|
|
435
441
|
{ is: 'reference', then: referenceFieldPartialSchema }
|
|
@@ -445,6 +451,7 @@ const fieldSchema: Joi.ObjectSchema<Field> = fieldCommonPropsSchema.when('.type'
|
|
|
445
451
|
switch: [
|
|
446
452
|
{ is: 'number', then: numberFieldPartialSchema },
|
|
447
453
|
{ is: 'enum', then: enumFieldPartialSchema },
|
|
454
|
+
{ is: 'image', then: imageFieldPartialSchema },
|
|
448
455
|
{ is: 'object', then: objectFieldPartialSchema },
|
|
449
456
|
{ is: 'model', then: modelFieldPartialSchema },
|
|
450
457
|
{ is: 'reference', then: referenceFieldPartialSchema },
|
|
@@ -187,6 +187,7 @@ export interface BaseDataModeList extends Omit<BaseDataModel, 'fields'> {
|
|
|
187
187
|
export interface FieldGroupItem {
|
|
188
188
|
name: string;
|
|
189
189
|
label: string;
|
|
190
|
+
icon?: string;
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
export interface ImageModel {
|
|
@@ -195,16 +196,18 @@ export interface ImageModel {
|
|
|
195
196
|
label?: string;
|
|
196
197
|
labelField?: string;
|
|
197
198
|
fields?: Field[];
|
|
199
|
+
source?: string;
|
|
198
200
|
}
|
|
199
201
|
|
|
200
202
|
/*******************
|
|
201
203
|
*** Field Types ***
|
|
202
204
|
*******************/
|
|
203
205
|
|
|
204
|
-
export type Field = FieldSimple | FieldEnum | FieldNumber | FieldObject | FieldModel | FieldReference | FieldStyle | FieldList;
|
|
206
|
+
export type Field = FieldSimple | FieldEnum | FieldImage | FieldNumber | FieldObject | FieldModel | FieldReference | FieldStyle | FieldList;
|
|
205
207
|
|
|
206
208
|
export type FieldSimple = FieldCommonProps & FieldSimpleProps;
|
|
207
209
|
export type FieldEnum = FieldCommonProps & FieldEnumProps;
|
|
210
|
+
export type FieldImage = FieldCommonProps & FieldImageProps;
|
|
208
211
|
export type FieldNumber = FieldCommonProps & FieldNumberProps;
|
|
209
212
|
export type FieldObject = FieldCommonProps & FieldObjectProps;
|
|
210
213
|
export type FieldModel = FieldCommonProps & FieldModelProps;
|
|
@@ -232,7 +235,7 @@ export interface FieldCommonProps {
|
|
|
232
235
|
export type FieldType = typeof FIELD_TYPES[number];
|
|
233
236
|
|
|
234
237
|
export interface FieldSimpleProps {
|
|
235
|
-
type: 'string' | 'url' | 'slug' | 'text' | 'markdown' | 'html' | 'boolean' | 'date' | 'datetime' | 'color' | '
|
|
238
|
+
type: 'string' | 'url' | 'slug' | 'text' | 'markdown' | 'html' | 'boolean' | 'date' | 'datetime' | 'color' | 'file' | 'json' | 'richText';
|
|
236
239
|
}
|
|
237
240
|
|
|
238
241
|
export type FieldEnumProps = FieldEnumDropdownProps | FieldEnumThumbnailsProps | FieldEnumPaletteProps;
|
|
@@ -272,6 +275,11 @@ export interface FieldEnumOptionPalette extends FieldEnumOptionObject {
|
|
|
272
275
|
borderColor?: string;
|
|
273
276
|
}
|
|
274
277
|
|
|
278
|
+
export interface FieldImageProps {
|
|
279
|
+
type: 'image';
|
|
280
|
+
source?: string;
|
|
281
|
+
}
|
|
282
|
+
|
|
275
283
|
export interface FieldNumberProps {
|
|
276
284
|
type: 'number';
|
|
277
285
|
controlType?: 'slider';
|
|
@@ -315,4 +323,4 @@ export interface FieldListProps {
|
|
|
315
323
|
items?: FieldListItems;
|
|
316
324
|
}
|
|
317
325
|
|
|
318
|
-
export type FieldListItems = FieldSimpleProps | FieldEnumProps | FieldNumberProps | FieldObjectProps | FieldModelProps | FieldReferenceProps;
|
|
326
|
+
export type FieldListItems = FieldSimpleProps | FieldEnumProps | FieldImageProps | FieldNumberProps | FieldObjectProps | FieldModelProps | FieldReferenceProps;
|