UNPKG

@stackbit/sdk

Version:
247 lines 11.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.mergeConfigModelsWithModelsFromFiles = exports.getYamlModelDirs = exports.loadYamlModelsFromFiles = exports.convertToYamlConfig = exports.isStackbitYamlFile = exports.findStackbitConfigFile = exports.loadConfigFromStackbitYaml = exports.loadStackbitYamlFromDir = exports.LATEST_STACKBIT_VERSION = exports.STACKBIT_CONFIG_FILES = exports.STACKBIT_CONFIG_JS_FILES = exports.STACKBIT_CONFIG_YAML_FILES = void 0; const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const js_yaml_1 = __importDefault(require("js-yaml")); const lodash_1 = __importDefault(require("lodash")); const utils_1 = require("@stackbit/utils"); const config_errors_1 = require("./config-errors"); const utils_2 = require("../utils"); exports.STACKBIT_CONFIG_YAML_FILES = ['stackbit.yaml', 'stackbit.yml']; exports.STACKBIT_CONFIG_JS_FILES = ['stackbit.config.js', 'stackbit.config.cjs', 'stackbit.config.mjs', 'stackbit.config.ts']; exports.STACKBIT_CONFIG_FILES = [...exports.STACKBIT_CONFIG_YAML_FILES, ...exports.STACKBIT_CONFIG_JS_FILES]; exports.LATEST_STACKBIT_VERSION = '0.7.0'; async function loadStackbitYamlFromDir(dirPath) { const stackbitYamlPath = await (0, utils_1.getFirstExistingFile)(exports.STACKBIT_CONFIG_YAML_FILES, dirPath); if (!stackbitYamlPath) { return { config: null, error: new config_errors_1.StackbitConfigNotFoundError() }; } return await loadConfigFromStackbitYaml(stackbitYamlPath); } exports.loadStackbitYamlFromDir = loadStackbitYamlFromDir; async function loadConfigFromStackbitYaml(stackbitYamlPath) { const stackbitYaml = await fs_extra_1.default.readFile(stackbitYamlPath, 'utf8'); const config = js_yaml_1.default.load(stackbitYaml, { schema: js_yaml_1.default.JSON_SCHEMA }); if (!config || typeof config !== 'object') { const fileName = path_1.default.basename(stackbitYamlPath); return { config: null, error: new config_errors_1.ConfigLoadError(`error parsing ${fileName}, ${config_errors_1.REFER_TO_STACKBIT_CONFIG_DOCS}`) }; } return { config: config, error: null }; } exports.loadConfigFromStackbitYaml = loadConfigFromStackbitYaml; async function findStackbitConfigFile(dirs) { for (const dir of dirs) { for (const fileName of exports.STACKBIT_CONFIG_FILES) { const filePath = path_1.default.resolve(dir, fileName); if (await fs_extra_1.default.pathExists(filePath)) { return filePath; } } } return null; } exports.findStackbitConfigFile = findStackbitConfigFile; function isStackbitYamlFile(filePath) { const pathObject = path_1.default.parse(filePath); return pathObject.base === 'stackbit.yaml' || pathObject.dir.split(path_1.default.sep).includes('.stackbit'); } exports.isStackbitYamlFile = isStackbitYamlFile; function convertToYamlConfig({ config }) { const yamlConfig = lodash_1.default.cloneDeep(lodash_1.default.omit(config, ['models', 'dirPath', 'filePath', 'presets'])); if (!lodash_1.default.isEmpty(config.models)) { yamlConfig.models = lodash_1.default.reduce(config.models, (yamlModels, model) => { const yamlModel = lodash_1.default.omit(model, ['name', '__metadata', 'presets']); switch (yamlModel.type) { case 'page': if (!yamlModel.hideContent && yamlModel.fields) { lodash_1.default.remove(yamlModel.fields, (field) => field.name === 'markdown_content'); } if (yamlModel.fields) { lodash_1.default.remove(yamlModel.fields, (field) => field.name === (config.pageLayoutKey || 'layout')); } yamlModels[model.name] = yamlModel; break; case 'data': if (yamlModel.fields) { lodash_1.default.remove(yamlModel.fields, (field) => field.name === (config.objectTypeKey || 'type')); } break; case 'object': case 'config': yamlModels[model.name] = yamlModel; break; default: { const _exhaustiveCheck = yamlModel; return _exhaustiveCheck; } } yamlModels[model.name] = yamlModel; return yamlModels; }, {}); } return yamlConfig; } exports.convertToYamlConfig = convertToYamlConfig; async function loadYamlModelsFromFiles(config) { const dirPath = config.dirPath; const modelDirs = getYamlModelDirs(config); const modelFiles = await (0, utils_1.reducePromise)(modelDirs, async (modelFiles, modelDir) => { const absModelsDir = path_1.default.join(dirPath, modelDir); const dirExists = await fs_extra_1.default.pathExists(absModelsDir); if (!dirExists) { return modelFiles; } const files = await readYamlModelFilesFromDir(absModelsDir); return modelFiles.concat(files.map((filePath) => path_1.default.join(modelDir, filePath))); }, []); const result = await (0, utils_1.reducePromise)(modelFiles, async (result, modelFile) => { let model; try { model = await (0, utils_1.parseFile)(path_1.default.join(dirPath, modelFile)); } catch (error) { return { modelMap: result.modelMap, errors: result.errors.concat(new config_errors_1.ModelLoadError(`error parsing model, file: ${modelFile}`)) }; } const modelName = model?.name; if (!modelName) { return { modelMap: result.modelMap, errors: result.errors.concat(new config_errors_1.ModelLoadError(`model does not have a name, file: ${modelFile}`)) }; } result.modelMap[modelName] = { __metadata: { filePath: modelFile }, ...model }; return result; }, { modelMap: {}, errors: [] }); return { models: Object.values(result.modelMap), errors: result.errors }; } exports.loadYamlModelsFromFiles = loadYamlModelsFromFiles; function getYamlModelDirs(config) { const modelsSource = lodash_1.default.get(config, 'modelsSource', {}); const sourceType = lodash_1.default.get(modelsSource, 'type', 'files'); const defaultModelDirs = ['node_modules/@stackbit/components/models', '.stackbit/models']; const modelDirs = lodash_1.default.get(modelsSource, 'modelDirs', defaultModelDirs); return sourceType === 'files' ? lodash_1.default.castArray(modelDirs).map((modelDir) => lodash_1.default.trim(modelDir, '/')) : defaultModelDirs; } exports.getYamlModelDirs = getYamlModelDirs; async function readYamlModelFilesFromDir(modelsDir) { return await (0, utils_1.readDirRecursively)(modelsDir, { filter: (filePath, stats) => { if (stats.isDirectory()) { return true; } const extension = path_1.default.extname(filePath).substring(1); return stats.isFile() && ['yaml', 'yml'].includes(extension); } }); } function mergeConfigModelsWithModelsFromFiles(configModels, modelsFromFiles) { const configModelsByName = lodash_1.default.keyBy(configModels, 'name'); const mergedModelsFromFiles = modelsFromFiles.map((modelFromFile) => { // resolve thumbnails of models loaded from files const modelFilePath = modelFromFile.__metadata?.filePath; modelFromFile = resolveThumbnailPathForModelOrObjectField(modelFromFile, modelFilePath); modelFromFile = (0, utils_2.mapModelFieldsRecursively)(modelFromFile, (field) => { if ((0, utils_2.isListField)(field)) { field = (0, utils_2.normalizeListField)(field); field = { ...field, items: resolveThumbnailForEnumField(field.items, modelFilePath) }; } else { field = resolveThumbnailForEnumField(field, modelFilePath); } return field; }); const configModel = lodash_1.default.get(configModelsByName, modelFromFile.name); if (!configModel) { return modelFromFile; } return lodash_1.default.assign({}, modelFromFile, configModel, { fields: lodash_1.default.unionBy(configModel?.fields ?? [], modelFromFile?.fields ?? [], 'name') }); }); const mergedModels = lodash_1.default.unionBy(mergedModelsFromFiles, configModels, 'name'); // extend config models having the "extends" property // this must be done before any validation as some properties like // the labelField will not work when validating models without extending them first return (0, utils_2.extendModelArray)(mergedModels); } exports.mergeConfigModelsWithModelsFromFiles = mergeConfigModelsWithModelsFromFiles; function resolveThumbnailForEnumField(field, modelFilePath) { if ((0, utils_2.isObjectField)(field)) { field = resolveThumbnailPathForModelOrObjectField(field, modelFilePath); } else if ((0, utils_2.isEnumField)(field)) { field = resolveThumbnailPathForEnumField(field, modelFilePath); } return field; } function resolveThumbnailPathForModelOrObjectField(modelOrField, modelFilePath) { if (modelOrField.thumbnail && modelFilePath) { const modelDirPath = path_1.default.dirname(modelFilePath); modelOrField = { ...modelOrField, thumbnail: resolveThumbnailPath(modelOrField.thumbnail, modelDirPath) }; } return modelOrField; } function resolveThumbnailPathForEnumField(enumField, modelFilePath) { if (enumField.controlType === 'thumbnails' && modelFilePath) { const modelDirPath = path_1.default.dirname(modelFilePath); enumField = { ...enumField, options: lodash_1.default.map(enumField.options, (option) => { if (option.thumbnail) { option = { ...option, thumbnail: resolveThumbnailPath(option.thumbnail, modelDirPath) }; } return option; }) }; } return enumField; } function resolveThumbnailPath(thumbnail, modelDirPath) { if (thumbnail.startsWith('//') || /https?:\/\//.test(thumbnail)) { return thumbnail; } if (thumbnail.startsWith('/')) { if (modelDirPath.endsWith('@stackbit/components/models')) { modelDirPath = modelDirPath.replace(/\/models$/, ''); } else { modelDirPath = ''; } thumbnail = thumbnail.replace(/^\//, ''); } return path_1.default.join(modelDirPath, thumbnail); } //# sourceMappingURL=config-loader-utils.js.map