@stackbit/sdk 0.2.20 → 0.2.24

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 (36) hide show
  1. package/dist/config/config-consts.d.ts +1 -1
  2. package/dist/config/config-consts.js +2 -0
  3. package/dist/config/config-consts.js.map +1 -1
  4. package/dist/config/config-loader.d.ts +6 -4
  5. package/dist/config/config-loader.js +297 -100
  6. package/dist/config/config-loader.js.map +1 -1
  7. package/dist/config/config-schema.js +28 -7
  8. package/dist/config/config-schema.js.map +1 -1
  9. package/dist/config/config-types.d.ts +22 -5
  10. package/dist/config/config-writer.js +3 -0
  11. package/dist/config/config-writer.js.map +1 -1
  12. package/dist/config/presets-loader.js +18 -11
  13. package/dist/config/presets-loader.js.map +1 -1
  14. package/dist/content/content-loader.js +1 -1
  15. package/dist/content/content-schema.js +8 -0
  16. package/dist/content/content-schema.js.map +1 -1
  17. package/dist/utils/model-extender.js.map +1 -1
  18. package/dist/utils/model-iterators.d.ts +61 -1
  19. package/dist/utils/model-iterators.js +60 -11
  20. package/dist/utils/model-iterators.js.map +1 -1
  21. package/dist/utils/model-utils.d.ts +44 -3
  22. package/dist/utils/model-utils.js +93 -10
  23. package/dist/utils/model-utils.js.map +1 -1
  24. package/package.json +2 -2
  25. package/src/.DS_Store +0 -0
  26. package/src/config/config-consts.ts +2 -0
  27. package/src/config/config-loader.ts +333 -111
  28. package/src/config/config-schema.ts +35 -8
  29. package/src/config/config-types.ts +26 -5
  30. package/src/config/config-writer.ts +3 -0
  31. package/src/config/presets-loader.ts +19 -15
  32. package/src/content/content-loader.ts +2 -2
  33. package/src/content/content-schema.ts +9 -0
  34. package/src/utils/model-extender.ts +1 -1
  35. package/src/utils/model-iterators.ts +61 -13
  36. package/src/utils/model-utils.ts +91 -8
@@ -1,6 +1,6 @@
1
1
  export declare const SSG_NAMES: readonly ["unibit", "jekyll", "hugo", "gatsby", "nextjs", "custom", "eleventy", "vuepress", "gridsome", "nuxt", "sapper", "hexo"];
2
2
  export declare const CMS_NAMES: readonly ["git", "contentful", "sanity", "forestry", "netlifycms"];
3
- export declare const FIELD_TYPES: readonly ["string", "url", "slug", "text", "markdown", "html", "number", "boolean", "enum", "date", "datetime", "color", "image", "file", "object", "model", "reference", "style", "list"];
3
+ export declare const FIELD_TYPES: readonly ["string", "url", "slug", "text", "markdown", "html", "number", "boolean", "enum", "date", "datetime", "color", "image", "file", "json", "richText", "object", "model", "reference", "style", "list"];
4
4
  export declare const STYLE_PROPS: readonly ["objectFit", "objectPosition", "flexDirection", "justifyContent", "justifyItems", "justifySelf", "alignContent", "alignItems", "alignSelf", "padding", "margin", "width", "height", "fontFamily", "fontSize", "fontStyle", "fontWeight", "textAlign", "textColor", "textDecoration", "backgroundColor", "backgroundPosition", "backgroundSize", "borderRadius", "borderWidth", "borderColor", "borderStyle", "boxShadow", "opacity"];
5
5
  export declare const STYLE_PROPS_VALUES: {
6
6
  nineRegions: string[];
@@ -20,6 +20,8 @@ exports.FIELD_TYPES = [
20
20
  'color',
21
21
  'image',
22
22
  'file',
23
+ 'json',
24
+ 'richText',
23
25
  'object',
24
26
  'model',
25
27
  'reference',
@@ -1 +1 @@
1
- {"version":3,"file":"config-consts.js","sourceRoot":"","sources":["../../src/config/config-consts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config-consts.js","sourceRoot":"","sources":["../../src/config/config-consts.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,9 @@
1
1
  import { ConfigError, ConfigLoadError, ConfigValidationError } from './config-errors';
2
- import { Config } from './config-types';
2
+ import { Config, Model } from './config-types';
3
3
  export interface ConfigLoaderOptions {
4
+ [option: string]: any;
4
5
  dirPath: string;
6
+ modelsSource?: string;
5
7
  }
6
8
  export interface ConfigLoaderResult {
7
9
  valid: boolean;
@@ -14,8 +16,8 @@ export interface NormalizedValidationResult {
14
16
  errors: ConfigValidationError[];
15
17
  }
16
18
  export interface TempConfigLoaderResult {
17
- config?: any;
19
+ config?: Record<string, unknown>;
18
20
  errors: ConfigLoadError[];
19
21
  }
20
- export declare function loadConfig({ dirPath }: ConfigLoaderOptions): Promise<ConfigLoaderResult>;
21
- export declare function validateAndNormalizeConfig(config: any): NormalizedValidationResult;
22
+ export declare function loadConfig({ dirPath, ...options }: ConfigLoaderOptions): Promise<ConfigLoaderResult>;
23
+ export declare function validateAndNormalizeConfig(config: any, externalModels?: Model[]): NormalizedValidationResult;
@@ -1,4 +1,23 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
23
  };
@@ -14,65 +33,73 @@ const config_errors_1 = require("./config-errors");
14
33
  const utils_1 = require("../utils");
15
34
  const utils_2 = require("@stackbit/utils");
16
35
  const presets_loader_1 = require("./presets-loader");
17
- async function loadConfig({ dirPath }) {
18
- let configLoadResult;
19
- try {
20
- configLoadResult = await loadConfigFromDir(dirPath);
21
- }
22
- catch (error) {
23
- return {
24
- valid: false,
25
- config: null,
26
- errors: [new config_errors_1.ConfigLoadError(`Error loading Stackbit configuration: ${error.message}`, { originalError: error })]
27
- };
28
- }
29
- if (!configLoadResult.config) {
36
+ async function loadConfig({ dirPath, ...options }) {
37
+ const { config, errors: configLoadErrors } = await loadConfigFromDir(dirPath);
38
+ if (!config) {
30
39
  return {
31
40
  valid: false,
32
41
  config: null,
33
- errors: configLoadResult.errors
42
+ errors: configLoadErrors
34
43
  };
35
44
  }
36
- const normalizedResult = validateAndNormalizeConfig(configLoadResult.config);
45
+ const { models: externalModels, errors: externalModelsLoadErrors } = await loadModelsFromExternalSource(config, dirPath, options);
46
+ const normalizedResult = validateAndNormalizeConfig(config, externalModels);
37
47
  const presetsResult = await presets_loader_1.loadPresets(dirPath, normalizedResult.config);
38
48
  return {
39
49
  valid: normalizedResult.valid,
40
50
  config: presetsResult.config,
41
- errors: [...configLoadResult.errors, ...normalizedResult.errors, ...presetsResult.errors]
51
+ errors: [...configLoadErrors, ...externalModelsLoadErrors, ...normalizedResult.errors, ...presetsResult.errors]
42
52
  };
43
53
  }
44
54
  exports.loadConfig = loadConfig;
45
- function validateAndNormalizeConfig(config) {
46
- // validate the "contentModels" and extend config models with "contentModels"
47
- // this must be done before main config validation to make it independent of "contentModels".
48
- const contentModelsValidationResult = validateAndExtendContentModels(config);
49
- config = contentModelsValidationResult.value;
55
+ function validateAndNormalizeConfig(config, externalModels) {
50
56
  // extend config models having the "extends" property
51
- // this must be done before main config validation as some properties like
57
+ // this must be done before any validation as some properties like
52
58
  // the labelField will not work when validating models without extending them first
53
- const { models, errors: extendModelErrors } = utils_1.extendModelMap((config === null || config === void 0 ? void 0 : config.models) || {});
54
- config.models = models;
59
+ const { models: extendedModels, errors: extendModelErrors } = utils_1.extendModelMap(config.models);
60
+ const extendedConfig = {
61
+ ...config,
62
+ models: extendedModels
63
+ };
64
+ const { config: mergedConfig, errors: externalModelsMergeErrors } = mergeConfigWithExternalModels(extendedConfig, externalModels);
65
+ // validate the "contentModels" and extend config models with "contentModels"
66
+ // this must be done before main config validation to make it independent of "contentModels".
67
+ const { value: configWithContentModels, errors: contentModelsErrors } = validateAndExtendContentModels(mergedConfig);
55
68
  // normalize config - backward compatibility updates, adding extra fields like "markdown_content", "type" and "layout",
56
69
  // and setting other default values.
57
- config = normalizeConfig(config);
70
+ const normalizedConfig = normalizeConfig(configWithContentModels);
58
71
  // validate config
59
- const configValidationResult = config_validator_1.validateConfig(config);
60
- const errors = [...contentModelsValidationResult.errors, ...extendModelErrors, ...configValidationResult.errors];
72
+ const { value: validatedConfig, errors: validationErrors } = config_validator_1.validateConfig(normalizedConfig);
73
+ const errors = [...extendModelErrors, ...externalModelsMergeErrors, ...contentModelsErrors, ...validationErrors];
61
74
  return normalizeValidationResult({
62
75
  valid: lodash_1.default.isEmpty(errors),
63
- value: configValidationResult.value,
76
+ value: validatedConfig,
64
77
  errors: errors
65
78
  });
66
79
  }
67
80
  exports.validateAndNormalizeConfig = validateAndNormalizeConfig;
68
81
  async function loadConfigFromDir(dirPath) {
69
- const { config, error } = await loadConfigFromStackbitYaml(dirPath);
70
- if (error) {
71
- return { errors: [error] };
82
+ var _a;
83
+ try {
84
+ const { config, error } = await loadConfigFromStackbitYaml(dirPath);
85
+ if (error) {
86
+ return { errors: [error] };
87
+ }
88
+ const { models: modelsFromFiles, errors: fileModelsErrors } = await loadModelsFromFiles(dirPath, config);
89
+ const mergedModels = mergeConfigModelsWithModelsFromFiles((_a = config.models) !== null && _a !== void 0 ? _a : {}, modelsFromFiles);
90
+ return {
91
+ config: {
92
+ ...config,
93
+ models: mergedModels
94
+ },
95
+ errors: fileModelsErrors
96
+ };
97
+ }
98
+ catch (error) {
99
+ return {
100
+ errors: [new config_errors_1.ConfigLoadError(`Error loading Stackbit configuration: ${error.message}`, { originalError: error })]
101
+ };
72
102
  }
73
- const externalModelsResult = await loadExternalModels(dirPath, config);
74
- config.models = lodash_1.default.assign(externalModelsResult.models, config.models);
75
- return { config, errors: externalModelsResult.errors };
76
103
  }
77
104
  async function loadConfigFromStackbitYaml(dirPath) {
78
105
  const stackbitYamlPath = path_1.default.join(dirPath, 'stackbit.yaml');
@@ -91,47 +118,46 @@ async function loadConfigFromStackbitYaml(dirPath) {
91
118
  }
92
119
  return { config };
93
120
  }
94
- async function loadExternalModels(dirPath, config) {
121
+ async function loadModelsFromFiles(dirPath, config) {
95
122
  const modelsSource = lodash_1.default.get(config, 'modelsSource', {});
96
123
  const sourceType = lodash_1.default.get(modelsSource, 'type', 'files');
97
- if (sourceType === 'files') {
98
- const defaultModelDirs = ['node_modules/@stackbit/components/models', '.stackbit/models'];
99
- const modelDirs = lodash_1.default.castArray(lodash_1.default.get(modelsSource, 'modelDirs', defaultModelDirs)).map((modelDir) => lodash_1.default.trim(modelDir, '/'));
100
- const modelFiles = await utils_2.reducePromise(modelDirs, async (modelFiles, modelDir) => {
101
- const absModelsDir = path_1.default.join(dirPath, modelDir);
102
- const dirExists = await fs_extra_1.default.pathExists(absModelsDir);
103
- if (!dirExists) {
104
- return modelFiles;
105
- }
106
- const files = await readModelFilesFromDir(absModelsDir);
107
- return modelFiles.concat(files.map((filePath) => path_1.default.join(modelDir, filePath)));
108
- }, []);
109
- return utils_2.reducePromise(modelFiles, async (result, modelFile) => {
110
- let model;
111
- try {
112
- model = await utils_2.parseFile(path_1.default.join(dirPath, modelFile));
113
- }
114
- catch (error) {
115
- return {
116
- models: result.models,
117
- errors: result.errors.concat(new config_errors_1.ConfigLoadError(`error parsing model, file: ${modelFile}`))
118
- };
119
- }
120
- const modelName = model === null || model === void 0 ? void 0 : model.name;
121
- if (!modelName) {
122
- return {
123
- models: result.models,
124
- errors: result.errors.concat(new config_errors_1.ConfigLoadError(`model does not have a name, file: ${modelFile}`))
125
- };
126
- }
127
- result.models[modelName] = lodash_1.default.omit(model, 'name');
128
- result.models[modelName].__metadata = {
129
- filePath: modelFile
124
+ const defaultModelDirs = ['node_modules/@stackbit/components/models', '.stackbit/models'];
125
+ const modelDirs = sourceType === 'files'
126
+ ? lodash_1.default.castArray(lodash_1.default.get(modelsSource, 'modelDirs', defaultModelDirs)).map((modelDir) => lodash_1.default.trim(modelDir, '/'))
127
+ : defaultModelDirs;
128
+ const modelFiles = await utils_2.reducePromise(modelDirs, async (modelFiles, modelDir) => {
129
+ const absModelsDir = path_1.default.join(dirPath, modelDir);
130
+ const dirExists = await fs_extra_1.default.pathExists(absModelsDir);
131
+ if (!dirExists) {
132
+ return modelFiles;
133
+ }
134
+ const files = await readModelFilesFromDir(absModelsDir);
135
+ return modelFiles.concat(files.map((filePath) => path_1.default.join(modelDir, filePath)));
136
+ }, []);
137
+ return utils_2.reducePromise(modelFiles, async (result, modelFile) => {
138
+ let model;
139
+ try {
140
+ model = await utils_2.parseFile(path_1.default.join(dirPath, modelFile));
141
+ }
142
+ catch (error) {
143
+ return {
144
+ models: result.models,
145
+ errors: result.errors.concat(new config_errors_1.ConfigLoadError(`error parsing model, file: ${modelFile}`))
130
146
  };
131
- return result;
132
- }, { models: {}, errors: [] });
133
- }
134
- return { models: {}, errors: [] };
147
+ }
148
+ const modelName = model === null || model === void 0 ? void 0 : model.name;
149
+ if (!modelName) {
150
+ return {
151
+ models: result.models,
152
+ errors: result.errors.concat(new config_errors_1.ConfigLoadError(`model does not have a name, file: ${modelFile}`))
153
+ };
154
+ }
155
+ result.models[modelName] = lodash_1.default.omit(model, 'name');
156
+ result.models[modelName].__metadata = {
157
+ filePath: modelFile
158
+ };
159
+ return result;
160
+ }, { models: {}, errors: [] });
135
161
  }
136
162
  async function readModelFilesFromDir(modelsDir) {
137
163
  return await utils_2.readDirRecursively(modelsDir, {
@@ -144,6 +170,36 @@ async function readModelFilesFromDir(modelsDir) {
144
170
  }
145
171
  });
146
172
  }
173
+ async function loadModelsFromExternalSource(config, dirPath, options) {
174
+ var _a;
175
+ const modelsSource = lodash_1.default.get(config, 'modelsSource', {});
176
+ const sourceType = (_a = options.modelsSource) !== null && _a !== void 0 ? _a : lodash_1.default.get(modelsSource, 'type', 'files');
177
+ if (sourceType === 'files') {
178
+ return { models: [], errors: [] };
179
+ }
180
+ else if (sourceType === 'contentful') {
181
+ const contentfulModule = lodash_1.default.get(modelsSource, 'module', '@stackbit/cms-contentful');
182
+ const modulePath = path_1.default.join(dirPath, 'node_modules', contentfulModule);
183
+ const module = await Promise.resolve().then(() => __importStar(require(modulePath)));
184
+ try {
185
+ const { models } = await module.fetchAndConvertSchema(options);
186
+ return {
187
+ models: models,
188
+ errors: []
189
+ };
190
+ }
191
+ catch (error) {
192
+ return {
193
+ models: [],
194
+ errors: [new config_errors_1.ConfigLoadError(`Error fetching and converting Contentful schema, error: ${error.message}`, { originalError: error })]
195
+ };
196
+ }
197
+ }
198
+ return {
199
+ models: [],
200
+ errors: [new config_errors_1.ConfigLoadError(`modelsSource ${modelsSource} is unsupported`)]
201
+ };
202
+ }
147
203
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
148
204
  async function loadConfigFromDotStackbit(dirPath) {
149
205
  const stackbitDotPath = path_1.default.join(dirPath, '.stackbit');
@@ -172,6 +228,93 @@ async function loadConfigFromDotStackbit(dirPath) {
172
228
  }
173
229
  return lodash_1.default.isEmpty(config) ? null : config;
174
230
  }
231
+ function mergeConfigModelsWithModelsFromFiles(configModels, modelsFromFiles) {
232
+ const mergedModels = lodash_1.default.mapValues(modelsFromFiles, (modelFromFile, modelName) => {
233
+ var _a, _b, _c;
234
+ // resolve thumbnails of models loaded from files
235
+ const modelFilePath = (_a = modelFromFile.__metadata) === null || _a === void 0 ? void 0 : _a.filePath;
236
+ resolveThumbnailPathForModel(modelFromFile, modelFilePath);
237
+ utils_1.iterateModelFieldsRecursively(modelFromFile, (field) => {
238
+ if (utils_1.isListField(field)) {
239
+ field = utils_1.normalizeListFieldInPlace(field);
240
+ field = field.items;
241
+ }
242
+ if (utils_1.isObjectField(field)) {
243
+ resolveThumbnailPathForModel(field, modelFilePath);
244
+ }
245
+ else if (utils_1.isEnumField(field)) {
246
+ resolveThumbnailPathForEnumField(field, modelFilePath);
247
+ }
248
+ });
249
+ const configModel = lodash_1.default.get(configModels, modelName);
250
+ if (!configModel) {
251
+ return modelFromFile;
252
+ }
253
+ return lodash_1.default.assign({}, modelFromFile, configModel, {
254
+ fields: lodash_1.default.unionBy((_b = configModel === null || configModel === void 0 ? void 0 : configModel.fields) !== null && _b !== void 0 ? _b : [], (_c = modelFromFile === null || modelFromFile === void 0 ? void 0 : modelFromFile.fields) !== null && _c !== void 0 ? _c : [], 'name')
255
+ });
256
+ });
257
+ return Object.assign({}, configModels, mergedModels);
258
+ }
259
+ function mergeConfigWithExternalModels(config, externalModels) {
260
+ var _a;
261
+ if (!externalModels || externalModels.length === 0) {
262
+ return {
263
+ config,
264
+ errors: []
265
+ };
266
+ }
267
+ const stackbitModels = (_a = config === null || config === void 0 ? void 0 : config.models) !== null && _a !== void 0 ? _a : {};
268
+ const errors = [];
269
+ const models = lodash_1.default.reduce(externalModels, (modelMap, externalModel) => {
270
+ const { name, ...rest } = externalModel;
271
+ return Object.assign(modelMap, { [name]: rest });
272
+ }, {});
273
+ lodash_1.default.forEach(stackbitModels, (stackbitModel, modelName) => {
274
+ var _a, _b;
275
+ let externalModel = models[modelName];
276
+ if (!externalModel) {
277
+ return;
278
+ }
279
+ const modelType = stackbitModel.type ? (stackbitModel.type === 'config' ? 'data' : stackbitModel.type) : (_a = externalModel.type) !== null && _a !== void 0 ? _a : 'object';
280
+ const urlPath = modelType === 'page' ? (_b = stackbitModel === null || stackbitModel === void 0 ? void 0 : stackbitModel.urlPath) !== null && _b !== void 0 ? _b : '/{slug}' : null;
281
+ externalModel = Object.assign({}, externalModel, lodash_1.default.pick(stackbitModel, ['__metadata', 'label', 'description', 'thumbnail', 'singleInstance', 'readOnly', 'labelField', 'fieldGroups']), utils_2.omitByNil({
282
+ type: modelType,
283
+ urlPath
284
+ }));
285
+ externalModel = utils_1.mapModelFieldsRecursively(externalModel, (field, modelKeyPath) => {
286
+ const stackbitField = utils_1.getModelFieldForModelKeyPath(stackbitModel, modelKeyPath);
287
+ if (!stackbitField) {
288
+ return field;
289
+ }
290
+ let override = {};
291
+ if (stackbitField.type === 'style') {
292
+ override = stackbitField;
293
+ }
294
+ else if (field.type === 'enum') {
295
+ override = lodash_1.default.pick(stackbitField, ['options']);
296
+ }
297
+ else if (field.type === 'color') {
298
+ override = { type: 'color' };
299
+ }
300
+ else if (field.type === 'number') {
301
+ override = lodash_1.default.pick(stackbitField, ['subtype', 'min', 'max', 'step', 'unit']);
302
+ }
303
+ else if (field.type === 'object') {
304
+ override = lodash_1.default.pick(stackbitField, ['labelField', 'thumbnail', 'fieldGroups']);
305
+ }
306
+ return Object.assign({}, field, lodash_1.default.pick(stackbitField, ['label', 'description', 'required', 'default', 'group', 'const', 'hidden', 'readOnly', 'controlType']), override);
307
+ });
308
+ models[modelName] = externalModel;
309
+ });
310
+ return {
311
+ config: {
312
+ ...config,
313
+ models: models
314
+ },
315
+ errors: errors
316
+ };
317
+ }
175
318
  function normalizeConfig(config) {
176
319
  const pageLayoutKey = lodash_1.default.get(config, 'pageLayoutKey', 'layout');
177
320
  const objectTypeKey = lodash_1.default.get(config, 'objectTypeKey', 'type');
@@ -179,12 +322,19 @@ function normalizeConfig(config) {
179
322
  const ver = semver_1.default.coerce(stackbitYamlVersion);
180
323
  const isStackbitYamlV2 = ver ? semver_1.default.satisfies(ver, '<0.3.0') : false;
181
324
  const models = (config === null || config === void 0 ? void 0 : config.models) || {};
325
+ const gitCMS = isGitCMS(config);
182
326
  let referencedModelNames = [];
183
- lodash_1.default.forEach(models, (model) => {
184
- var _a;
327
+ lodash_1.default.forEach(models, (model, modelName) => {
185
328
  if (!model) {
186
329
  return;
187
330
  }
331
+ if (!lodash_1.default.has(model, 'type')) {
332
+ model.type = 'object';
333
+ }
334
+ // add model label if not set
335
+ if (!lodash_1.default.has(model, 'label')) {
336
+ model.label = lodash_1.default.startCase(modelName);
337
+ }
188
338
  if (lodash_1.default.has(model, 'fields') && !Array.isArray(model.fields)) {
189
339
  model.fields = [];
190
340
  }
@@ -192,12 +342,14 @@ function normalizeConfig(config) {
192
342
  // rename old 'template' property to 'layout'
193
343
  utils_2.rename(model, 'template', 'layout');
194
344
  updatePageUrlPath(model);
195
- updatePageFilePath(model, config);
196
- addMarkdownContentField(model);
197
- // TODO: update schema-editor to not show layout field
198
- addLayoutFieldToPageModel(model, pageLayoutKey);
345
+ if (gitCMS) {
346
+ updatePageFilePath(model, config);
347
+ addMarkdownContentField(model);
348
+ // TODO: update schema-editor to not show layout field
349
+ addLayoutFieldToPageModel(model, pageLayoutKey);
350
+ }
199
351
  }
200
- else if (utils_1.isDataModel(model)) {
352
+ else if (utils_1.isDataModel(model) && gitCMS) {
201
353
  updateDataFilePath(model, config);
202
354
  }
203
355
  if (utils_1.isListDataModel(model)) {
@@ -212,26 +364,17 @@ function normalizeConfig(config) {
212
364
  else if (!lodash_1.default.has(model, 'labelField')) {
213
365
  utils_1.assignLabelFieldIfNeeded(model);
214
366
  }
215
- resolveThumbnailPathForModel(model, (_a = model === null || model === void 0 ? void 0 : model.__metadata) === null || _a === void 0 ? void 0 : _a.filePath);
216
367
  utils_1.iterateModelFieldsRecursively(model, (field) => {
217
- var _a, _b;
218
368
  // add field label if label is not set
219
369
  if (!lodash_1.default.has(field, 'label')) {
220
370
  field.label = lodash_1.default.startCase(field.name);
221
371
  }
222
372
  if (utils_1.isListField(field)) {
223
- // 'items.type' of list field default to 'string', set it explicitly
224
- if (!lodash_1.default.has(field, 'items.type')) {
225
- lodash_1.default.set(field, 'items.type', 'string');
226
- }
227
- field = utils_1.getListItemsField(field);
373
+ field = utils_1.normalizeListFieldInPlace(field);
374
+ field = field.items;
228
375
  }
229
376
  if (utils_1.isObjectField(field)) {
230
377
  utils_1.assignLabelFieldIfNeeded(field);
231
- resolveThumbnailPathForModel(field, (_a = model === null || model === void 0 ? void 0 : model.__metadata) === null || _a === void 0 ? void 0 : _a.filePath);
232
- }
233
- else if (utils_1.isEnumField(field)) {
234
- resolveThumbnailPathForEnumField(field, (_b = model === null || model === void 0 ? void 0 : model.__metadata) === null || _b === void 0 ? void 0 : _b.filePath);
235
378
  }
236
379
  else if (utils_1.isCustomModelField(field, models)) {
237
380
  // stackbit v0.2.0 compatibility
@@ -262,7 +405,9 @@ function normalizeConfig(config) {
262
405
  field.models = lodash_1.default.get(field, 'models', []);
263
406
  }
264
407
  }
265
- referencedModelNames = lodash_1.default.union(referencedModelNames, getReferencedModelNames(field));
408
+ if (gitCMS) {
409
+ referencedModelNames = lodash_1.default.union(referencedModelNames, getReferencedModelNames(field));
410
+ }
266
411
  });
267
412
  });
268
413
  lodash_1.default.forEach(referencedModelNames, (modelName) => {
@@ -416,7 +561,7 @@ function resolveThumbnailPath(thumbnail, modelDirPath) {
416
561
  function getReferencedModelNames(field) {
417
562
  var _a, _b;
418
563
  if (utils_1.isListField(field)) {
419
- field = utils_1.getListItemsField(field);
564
+ field = utils_1.getListFieldItems(field);
420
565
  }
421
566
  // TODO: add type field to model fields inside container update/create object logic rather adding type to schema
422
567
  // 'object' models referenced by 'model' fields should have 'type' field
@@ -437,7 +582,9 @@ function validateAndExtendContentModels(config) {
437
582
  var _a, _b;
438
583
  const contentModels = (_a = config.contentModels) !== null && _a !== void 0 ? _a : {};
439
584
  const models = (_b = config.models) !== null && _b !== void 0 ? _b : {};
440
- if (lodash_1.default.isEmpty(contentModels)) {
585
+ const externalModels = !isGitCMS(config);
586
+ const emptyContentModels = lodash_1.default.isEmpty(contentModels);
587
+ if (externalModels || emptyContentModels) {
441
588
  return {
442
589
  valid: true,
443
590
  value: config,
@@ -446,16 +593,16 @@ function validateAndExtendContentModels(config) {
446
593
  }
447
594
  const validationResult = config_validator_1.validateContentModels(contentModels, models);
448
595
  if (lodash_1.default.isEmpty(models)) {
449
- return validationResult;
596
+ return {
597
+ valid: validationResult.valid,
598
+ value: config,
599
+ errors: validationResult.errors
600
+ };
450
601
  }
451
602
  const extendedModels = lodash_1.default.mapValues(models, (model, modelName) => {
452
603
  const contentModel = validationResult.value.contentModels[modelName];
453
604
  if (!contentModel) {
454
- return {
455
- // if a model does not define a type, use the default "object" type
456
- type: model.type || 'object',
457
- ...lodash_1.default.omit(model, 'type')
458
- };
605
+ return model;
459
606
  }
460
607
  if (lodash_1.default.get(contentModel, '__metadata.invalid')) {
461
608
  return model;
@@ -490,9 +637,41 @@ function validateAndExtendContentModels(config) {
490
637
  };
491
638
  }
492
639
  function normalizeValidationResult(validationResult) {
640
+ validationResult = filterAndOrderConfigFields(validationResult);
493
641
  convertModelGroupsToModelList(validationResult);
494
642
  return convertModelsToArray(validationResult);
495
643
  }
644
+ function filterAndOrderConfigFields(validationResult) {
645
+ // TODO: see if we move filtering and sorting to Joi
646
+ return {
647
+ ...validationResult,
648
+ value: lodash_1.default.pick(validationResult.value, [
649
+ 'stackbitVersion',
650
+ 'ssgName',
651
+ 'ssgVersion',
652
+ 'cmsName',
653
+ 'import',
654
+ 'buildCommand',
655
+ 'publishDir',
656
+ 'nodeVersion',
657
+ 'devCommand',
658
+ 'staticDir',
659
+ 'uploadDir',
660
+ 'assets',
661
+ 'pagesDir',
662
+ 'dataDir',
663
+ 'pageLayoutKey',
664
+ 'objectTypeKey',
665
+ 'styleObjectModelName',
666
+ 'excludePages',
667
+ 'logicFields',
668
+ 'contentModels',
669
+ 'modelsSource',
670
+ 'models',
671
+ 'presets'
672
+ ])
673
+ };
674
+ }
496
675
  function convertModelGroupsToModelList(validationResult) {
497
676
  var _a, _b;
498
677
  const models = (_b = (_a = validationResult.value) === null || _a === void 0 ? void 0 : _a.models) !== null && _b !== void 0 ? _b : {};
@@ -516,7 +695,7 @@ function convertModelGroupsToModelList(validationResult) {
516
695
  lodash_1.default.forEach(models, (model) => {
517
696
  utils_1.iterateModelFieldsRecursively(model, (field) => {
518
697
  if (utils_1.isListField(field)) {
519
- field = utils_1.getListItemsField(field);
698
+ field = field.items;
520
699
  }
521
700
  if (field.groups) {
522
701
  let key = null;
@@ -550,6 +729,9 @@ function convertModelsToArray(validationResult) {
550
729
  ...yamlModel
551
730
  };
552
731
  });
732
+ if (!isGitCMS(config)) {
733
+ addImageModel(modelArray);
734
+ }
553
735
  const convertedErrors = lodash_1.default.map(validationResult.errors, (error) => {
554
736
  if (error.fieldPath[0] === 'models' && typeof error.fieldPath[1] == 'string') {
555
737
  const modelName = error.fieldPath[1];
@@ -569,4 +751,19 @@ function convertModelsToArray(validationResult) {
569
751
  errors: convertedErrors
570
752
  };
571
753
  }
754
+ function addImageModel(models) {
755
+ models.push({
756
+ type: 'image',
757
+ name: '__image_model',
758
+ label: 'Image',
759
+ labelField: 'title',
760
+ fields: [
761
+ { name: 'title', type: 'string' },
762
+ { name: 'url', type: 'string' }
763
+ ]
764
+ });
765
+ }
766
+ function isGitCMS(config) {
767
+ return !config.cmsName || config.cmsName === 'git';
768
+ }
572
769
  //# sourceMappingURL=config-loader.js.map