@stackbit/sdk 0.2.21 → 0.2.25

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 +291 -99
  6. package/dist/config/config-loader.js.map +1 -1
  7. package/dist/config/config-schema.js +27 -3
  8. package/dist/config/config-schema.js.map +1 -1
  9. package/dist/config/config-types.d.ts +21 -4
  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 +328 -111
  28. package/src/config/config-schema.ts +34 -4
  29. package/src/config/config-types.ts +25 -4
  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, ModelsSource } from './config-types';
3
3
  export interface ConfigLoaderOptions {
4
+ [option: string]: any;
4
5
  dirPath: string;
6
+ modelsSource?: ModelsSource;
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,35 @@ async function readModelFilesFromDir(modelsDir) {
144
170
  }
145
171
  });
146
172
  }
173
+ async function loadModelsFromExternalSource(config, dirPath, options = {}) {
174
+ const modelsSource = lodash_1.default.get(config, 'modelsSource', options.modelsSource);
175
+ const sourceType = lodash_1.default.get(modelsSource, 'type', 'files');
176
+ if (sourceType === 'files') {
177
+ return { models: [], errors: [] };
178
+ }
179
+ else if (sourceType === 'contentful') {
180
+ const contentfulModule = lodash_1.default.get(modelsSource, 'module', '@stackbit/cms-contentful');
181
+ const modulePath = path_1.default.resolve(dirPath, 'node_modules', contentfulModule);
182
+ const module = await Promise.resolve().then(() => __importStar(require(modulePath)));
183
+ try {
184
+ const { models } = await module.fetchAndConvertSchema(lodash_1.default.omit(options, 'modelsSource'));
185
+ return {
186
+ models: models,
187
+ errors: []
188
+ };
189
+ }
190
+ catch (error) {
191
+ return {
192
+ models: [],
193
+ errors: [new config_errors_1.ConfigLoadError(`Error fetching and converting Contentful schema, error: ${error.message}`, { originalError: error })]
194
+ };
195
+ }
196
+ }
197
+ return {
198
+ models: [],
199
+ errors: [new config_errors_1.ConfigLoadError(`modelsSource ${modelsSource} is unsupported`)]
200
+ };
201
+ }
147
202
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
148
203
  async function loadConfigFromDotStackbit(dirPath) {
149
204
  const stackbitDotPath = path_1.default.join(dirPath, '.stackbit');
@@ -172,6 +227,93 @@ async function loadConfigFromDotStackbit(dirPath) {
172
227
  }
173
228
  return lodash_1.default.isEmpty(config) ? null : config;
174
229
  }
230
+ function mergeConfigModelsWithModelsFromFiles(configModels, modelsFromFiles) {
231
+ const mergedModels = lodash_1.default.mapValues(modelsFromFiles, (modelFromFile, modelName) => {
232
+ var _a, _b, _c;
233
+ // resolve thumbnails of models loaded from files
234
+ const modelFilePath = (_a = modelFromFile.__metadata) === null || _a === void 0 ? void 0 : _a.filePath;
235
+ resolveThumbnailPathForModel(modelFromFile, modelFilePath);
236
+ utils_1.iterateModelFieldsRecursively(modelFromFile, (field) => {
237
+ if (utils_1.isListField(field)) {
238
+ field = utils_1.normalizeListFieldInPlace(field);
239
+ field = field.items;
240
+ }
241
+ if (utils_1.isObjectField(field)) {
242
+ resolveThumbnailPathForModel(field, modelFilePath);
243
+ }
244
+ else if (utils_1.isEnumField(field)) {
245
+ resolveThumbnailPathForEnumField(field, modelFilePath);
246
+ }
247
+ });
248
+ const configModel = lodash_1.default.get(configModels, modelName);
249
+ if (!configModel) {
250
+ return modelFromFile;
251
+ }
252
+ return lodash_1.default.assign({}, modelFromFile, configModel, {
253
+ 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')
254
+ });
255
+ });
256
+ return Object.assign({}, configModels, mergedModels);
257
+ }
258
+ function mergeConfigWithExternalModels(config, externalModels) {
259
+ var _a;
260
+ if (!externalModels || externalModels.length === 0) {
261
+ return {
262
+ config,
263
+ errors: []
264
+ };
265
+ }
266
+ const stackbitModels = (_a = config === null || config === void 0 ? void 0 : config.models) !== null && _a !== void 0 ? _a : {};
267
+ const errors = [];
268
+ const models = lodash_1.default.reduce(externalModels, (modelMap, externalModel) => {
269
+ const { name, ...rest } = externalModel;
270
+ return Object.assign(modelMap, { [name]: rest });
271
+ }, {});
272
+ lodash_1.default.forEach(stackbitModels, (stackbitModel, modelName) => {
273
+ var _a, _b;
274
+ let externalModel = models[modelName];
275
+ if (!externalModel) {
276
+ return;
277
+ }
278
+ const modelType = stackbitModel.type ? (stackbitModel.type === 'config' ? 'data' : stackbitModel.type) : (_a = externalModel.type) !== null && _a !== void 0 ? _a : 'object';
279
+ const urlPath = modelType === 'page' ? (_b = stackbitModel === null || stackbitModel === void 0 ? void 0 : stackbitModel.urlPath) !== null && _b !== void 0 ? _b : '/{slug}' : null;
280
+ externalModel = Object.assign({}, externalModel, lodash_1.default.pick(stackbitModel, ['__metadata', 'label', 'description', 'thumbnail', 'singleInstance', 'readOnly', 'labelField', 'fieldGroups']), utils_2.omitByNil({
281
+ type: modelType,
282
+ urlPath
283
+ }));
284
+ externalModel = utils_1.mapModelFieldsRecursively(externalModel, (field, modelKeyPath) => {
285
+ const stackbitField = utils_1.getModelFieldForModelKeyPath(stackbitModel, modelKeyPath);
286
+ if (!stackbitField) {
287
+ return field;
288
+ }
289
+ let override = {};
290
+ if (stackbitField.type === 'style') {
291
+ override = stackbitField;
292
+ }
293
+ else if (field.type === 'enum') {
294
+ override = lodash_1.default.pick(stackbitField, ['options']);
295
+ }
296
+ else if (field.type === 'color') {
297
+ override = { type: 'color' };
298
+ }
299
+ else if (field.type === 'number') {
300
+ override = lodash_1.default.pick(stackbitField, ['subtype', 'min', 'max', 'step', 'unit']);
301
+ }
302
+ else if (field.type === 'object') {
303
+ override = lodash_1.default.pick(stackbitField, ['labelField', 'thumbnail', 'fieldGroups']);
304
+ }
305
+ return Object.assign({}, field, lodash_1.default.pick(stackbitField, ['label', 'description', 'required', 'default', 'group', 'const', 'hidden', 'readOnly', 'controlType']), override);
306
+ });
307
+ models[modelName] = externalModel;
308
+ });
309
+ return {
310
+ config: {
311
+ ...config,
312
+ models: models
313
+ },
314
+ errors: errors
315
+ };
316
+ }
175
317
  function normalizeConfig(config) {
176
318
  const pageLayoutKey = lodash_1.default.get(config, 'pageLayoutKey', 'layout');
177
319
  const objectTypeKey = lodash_1.default.get(config, 'objectTypeKey', 'type');
@@ -179,12 +321,15 @@ function normalizeConfig(config) {
179
321
  const ver = semver_1.default.coerce(stackbitYamlVersion);
180
322
  const isStackbitYamlV2 = ver ? semver_1.default.satisfies(ver, '<0.3.0') : false;
181
323
  const models = (config === null || config === void 0 ? void 0 : config.models) || {};
324
+ const gitCMS = isGitCMS(config);
182
325
  let referencedModelNames = [];
183
326
  lodash_1.default.forEach(models, (model, modelName) => {
184
- var _a;
185
327
  if (!model) {
186
328
  return;
187
329
  }
330
+ if (!lodash_1.default.has(model, 'type')) {
331
+ model.type = 'object';
332
+ }
188
333
  // add model label if not set
189
334
  if (!lodash_1.default.has(model, 'label')) {
190
335
  model.label = lodash_1.default.startCase(modelName);
@@ -196,12 +341,14 @@ function normalizeConfig(config) {
196
341
  // rename old 'template' property to 'layout'
197
342
  utils_2.rename(model, 'template', 'layout');
198
343
  updatePageUrlPath(model);
199
- updatePageFilePath(model, config);
200
- addMarkdownContentField(model);
201
- // TODO: update schema-editor to not show layout field
202
- addLayoutFieldToPageModel(model, pageLayoutKey);
344
+ if (gitCMS) {
345
+ updatePageFilePath(model, config);
346
+ addMarkdownContentField(model);
347
+ // TODO: update schema-editor to not show layout field
348
+ addLayoutFieldToPageModel(model, pageLayoutKey);
349
+ }
203
350
  }
204
- else if (utils_1.isDataModel(model)) {
351
+ else if (utils_1.isDataModel(model) && gitCMS) {
205
352
  updateDataFilePath(model, config);
206
353
  }
207
354
  if (utils_1.isListDataModel(model)) {
@@ -216,26 +363,17 @@ function normalizeConfig(config) {
216
363
  else if (!lodash_1.default.has(model, 'labelField')) {
217
364
  utils_1.assignLabelFieldIfNeeded(model);
218
365
  }
219
- resolveThumbnailPathForModel(model, (_a = model === null || model === void 0 ? void 0 : model.__metadata) === null || _a === void 0 ? void 0 : _a.filePath);
220
366
  utils_1.iterateModelFieldsRecursively(model, (field) => {
221
- var _a, _b;
222
367
  // add field label if label is not set
223
368
  if (!lodash_1.default.has(field, 'label')) {
224
369
  field.label = lodash_1.default.startCase(field.name);
225
370
  }
226
371
  if (utils_1.isListField(field)) {
227
- // 'items.type' of list field default to 'string', set it explicitly
228
- if (!lodash_1.default.has(field, 'items.type')) {
229
- lodash_1.default.set(field, 'items.type', 'string');
230
- }
231
- field = utils_1.getListItemsField(field);
372
+ field = utils_1.normalizeListFieldInPlace(field);
373
+ field = field.items;
232
374
  }
233
375
  if (utils_1.isObjectField(field)) {
234
376
  utils_1.assignLabelFieldIfNeeded(field);
235
- resolveThumbnailPathForModel(field, (_a = model === null || model === void 0 ? void 0 : model.__metadata) === null || _a === void 0 ? void 0 : _a.filePath);
236
- }
237
- else if (utils_1.isEnumField(field)) {
238
- resolveThumbnailPathForEnumField(field, (_b = model === null || model === void 0 ? void 0 : model.__metadata) === null || _b === void 0 ? void 0 : _b.filePath);
239
377
  }
240
378
  else if (utils_1.isCustomModelField(field, models)) {
241
379
  // stackbit v0.2.0 compatibility
@@ -266,7 +404,9 @@ function normalizeConfig(config) {
266
404
  field.models = lodash_1.default.get(field, 'models', []);
267
405
  }
268
406
  }
269
- referencedModelNames = lodash_1.default.union(referencedModelNames, getReferencedModelNames(field));
407
+ if (gitCMS) {
408
+ referencedModelNames = lodash_1.default.union(referencedModelNames, getReferencedModelNames(field));
409
+ }
270
410
  });
271
411
  });
272
412
  lodash_1.default.forEach(referencedModelNames, (modelName) => {
@@ -420,7 +560,7 @@ function resolveThumbnailPath(thumbnail, modelDirPath) {
420
560
  function getReferencedModelNames(field) {
421
561
  var _a, _b;
422
562
  if (utils_1.isListField(field)) {
423
- field = utils_1.getListItemsField(field);
563
+ field = utils_1.getListFieldItems(field);
424
564
  }
425
565
  // TODO: add type field to model fields inside container update/create object logic rather adding type to schema
426
566
  // 'object' models referenced by 'model' fields should have 'type' field
@@ -441,7 +581,9 @@ function validateAndExtendContentModels(config) {
441
581
  var _a, _b;
442
582
  const contentModels = (_a = config.contentModels) !== null && _a !== void 0 ? _a : {};
443
583
  const models = (_b = config.models) !== null && _b !== void 0 ? _b : {};
444
- if (lodash_1.default.isEmpty(contentModels)) {
584
+ const externalModels = !isGitCMS(config);
585
+ const emptyContentModels = lodash_1.default.isEmpty(contentModels);
586
+ if (externalModels || emptyContentModels) {
445
587
  return {
446
588
  valid: true,
447
589
  value: config,
@@ -450,16 +592,16 @@ function validateAndExtendContentModels(config) {
450
592
  }
451
593
  const validationResult = config_validator_1.validateContentModels(contentModels, models);
452
594
  if (lodash_1.default.isEmpty(models)) {
453
- return validationResult;
595
+ return {
596
+ valid: validationResult.valid,
597
+ value: config,
598
+ errors: validationResult.errors
599
+ };
454
600
  }
455
601
  const extendedModels = lodash_1.default.mapValues(models, (model, modelName) => {
456
602
  const contentModel = validationResult.value.contentModels[modelName];
457
603
  if (!contentModel) {
458
- return {
459
- // if a model does not define a type, use the default "object" type
460
- type: model.type || 'object',
461
- ...lodash_1.default.omit(model, 'type')
462
- };
604
+ return model;
463
605
  }
464
606
  if (lodash_1.default.get(contentModel, '__metadata.invalid')) {
465
607
  return model;
@@ -494,9 +636,41 @@ function validateAndExtendContentModels(config) {
494
636
  };
495
637
  }
496
638
  function normalizeValidationResult(validationResult) {
639
+ validationResult = filterAndOrderConfigFields(validationResult);
497
640
  convertModelGroupsToModelList(validationResult);
498
641
  return convertModelsToArray(validationResult);
499
642
  }
643
+ function filterAndOrderConfigFields(validationResult) {
644
+ // TODO: see if we move filtering and sorting to Joi
645
+ return {
646
+ ...validationResult,
647
+ value: lodash_1.default.pick(validationResult.value, [
648
+ 'stackbitVersion',
649
+ 'ssgName',
650
+ 'ssgVersion',
651
+ 'cmsName',
652
+ 'import',
653
+ 'buildCommand',
654
+ 'publishDir',
655
+ 'nodeVersion',
656
+ 'devCommand',
657
+ 'staticDir',
658
+ 'uploadDir',
659
+ 'assets',
660
+ 'pagesDir',
661
+ 'dataDir',
662
+ 'pageLayoutKey',
663
+ 'objectTypeKey',
664
+ 'styleObjectModelName',
665
+ 'excludePages',
666
+ 'logicFields',
667
+ 'contentModels',
668
+ 'modelsSource',
669
+ 'models',
670
+ 'presets'
671
+ ])
672
+ };
673
+ }
500
674
  function convertModelGroupsToModelList(validationResult) {
501
675
  var _a, _b;
502
676
  const models = (_b = (_a = validationResult.value) === null || _a === void 0 ? void 0 : _a.models) !== null && _b !== void 0 ? _b : {};
@@ -520,7 +694,7 @@ function convertModelGroupsToModelList(validationResult) {
520
694
  lodash_1.default.forEach(models, (model) => {
521
695
  utils_1.iterateModelFieldsRecursively(model, (field) => {
522
696
  if (utils_1.isListField(field)) {
523
- field = utils_1.getListItemsField(field);
697
+ field = field.items;
524
698
  }
525
699
  if (field.groups) {
526
700
  let key = null;
@@ -554,6 +728,9 @@ function convertModelsToArray(validationResult) {
554
728
  ...yamlModel
555
729
  };
556
730
  });
731
+ if (!isGitCMS(config)) {
732
+ addImageModel(modelArray);
733
+ }
557
734
  const convertedErrors = lodash_1.default.map(validationResult.errors, (error) => {
558
735
  if (error.fieldPath[0] === 'models' && typeof error.fieldPath[1] == 'string') {
559
736
  const modelName = error.fieldPath[1];
@@ -573,4 +750,19 @@ function convertModelsToArray(validationResult) {
573
750
  errors: convertedErrors
574
751
  };
575
752
  }
753
+ function addImageModel(models) {
754
+ models.push({
755
+ type: 'image',
756
+ name: '__image_model',
757
+ label: 'Image',
758
+ labelField: 'title',
759
+ fields: [
760
+ { name: 'title', type: 'string' },
761
+ { name: 'url', type: 'string' }
762
+ ]
763
+ });
764
+ }
765
+ function isGitCMS(config) {
766
+ return !config.cmsName || config.cmsName === 'git';
767
+ }
576
768
  //# sourceMappingURL=config-loader.js.map