UNPKG

@stackbit/sdk

Version:
136 lines (123 loc) 4.92 kB
import _ from 'lodash'; import path from 'path'; import fse from 'fs-extra'; import { parseFile, append } from '@stackbit/utils'; import { ModelWithSource } from '@stackbit/types'; import { Config, Model, Preset } from './config-types'; import { ConfigPresetsError } from './config-errors'; interface RawPresetData { model: string; srcType: string; srcProjectId: string; presets: RawPreset[]; } interface RawPreset { label: string; thumbnail?: string; locale?: string; data: Record<string, any>; } export async function loadPresets({ config, fallbackSrcType, fallbackSrcProjectId }: { config: Config; fallbackSrcType?: string; fallbackSrcProjectId?: string; }): Promise<{ presets: Record<string, Preset>; errors: ConfigPresetsError[]; }> { const presetFiles = []; const presetsRelDirs = getPresetDirs(config); for (const presetsRelDir of presetsRelDirs) { const presetsDir = path.join(config.dirPath, presetsRelDir); if (!(await fse.pathExists(presetsDir))) { continue; } const files = (await fse.readdir(presetsDir)) .filter((fileName) => ['.json', '.yaml', '.yml'].includes(path.parse(fileName).ext)) .map((fileName) => path.join(presetsRelDir, fileName)); presetFiles.push(...files); } const presets: Record<string, Preset> = {}; const errors: ConfigPresetsError[] = []; for (const presetFile of presetFiles) { const presetsRelDir = path.dirname(presetFile); const presetPath = path.join(config.dirPath, presetFile); let presetData: RawPresetData; try { presetData = await parseFile(presetPath); } catch (err: any) { errors.push(new ConfigPresetsError(`Error parsing ${presetFile} (${err?.message})`)); continue; } const srcType = presetData.srcType ?? fallbackSrcType; const srcProjectId = presetData.srcProjectId ?? fallbackSrcProjectId; _.forEach(_.get(presetData, 'presets', []), (preset, i) => { const presetId = `${presetFile}:presets[${i}]`; const { thumbnail, ...rest } = preset; presets[presetId] = { ...rest, ...(thumbnail ? { thumbnail: resolveThumbnailPath(thumbnail, presetsRelDir) } : null), modelName: presetData.model, ...(srcType ? { srcType } : null), ...(srcProjectId ? { srcProjectId } : null) } as Preset; }); } return { presets, errors }; } export function getPresetDirs(config: Config): string[] { const defaultPresetsDirs = ['.stackbit/presets', 'node_modules/@stackbit/components/presets']; if (config.presetSource?.type === 'files' && config.presetSource?.presetDirs) { return [...new Set([...defaultPresetsDirs, ...config.presetSource.presetDirs])]; } return defaultPresetsDirs; } export function extendModelsWithPresetsIds<T extends Model | ModelWithSource>({ models, presets }: { models: T[]; presets: Record<string, Preset> }): T[] { // for older projects that pass the Model, we assume that all presets belong to the same content-source const presetsIdsByContentSourceAndModel: Record<string, Record<string, Record<string, string[]>>> = {}; const presetsIdsByModel_deprecated: Record<string, string[]> = {}; for (const [presetId, preset] of Object.entries(presets)) { append(presetsIdsByContentSourceAndModel, [preset.srcType, preset.srcProjectId, preset.modelName], presetId); append(presetsIdsByModel_deprecated, preset.modelName, presetId); } // update models with presets IDs return models.map((model) => { let presetIdsForModel: string[] | undefined; if ('srcType' in model && 'srcProjectId' in model) { // "as" can be removed in typescript 4.3.5 and above const srcType = (model as ModelWithSource).srcType; const srcProjectId = (model as ModelWithSource).srcProjectId; presetIdsForModel = presetsIdsByContentSourceAndModel[srcType]?.[srcProjectId]?.[model.name]; } else { presetIdsForModel = presetsIdsByModel_deprecated[model.name]; } if (!presetIdsForModel) { return model; } return { ...model, presets: presetIdsForModel }; }); } function resolveThumbnailPath(thumbnail: string, dir: string) { if (thumbnail.startsWith('//') || /https?:\/\//.test(thumbnail)) { return thumbnail; } if (thumbnail.startsWith('/')) { if (dir.endsWith('@stackbit/components/presets')) { dir = dir.replace(/\/presets$/, ''); } else { dir = ''; } thumbnail = thumbnail.replace(/^\//, ''); } return path.join(dir, thumbnail); }