UNPKG

sb-mig

Version:

CLI to rule the world. (and handle stuff related to Storyblok CMS)

269 lines (268 loc) 10.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.syncComponentsData = syncComponentsData; const array_utils_js_1 = require("../../utils/array-utils.js"); const logger_js_1 = __importDefault(require("../../utils/logger.js")); const object_utils_js_1 = require("../../utils/object-utils.js"); /** * Default progress callback that logs to console */ const defaultProgress = (event) => { if (event.type === "start") { logger_js_1.default.log(`Starting sync of ${event.total} components...`); } else if (event.type === "progress" && event.name) { const status = event.action === "creating" ? "Creating" : event.action === "updating" ? "Updating" : event.action === "created" ? "✓ Created" : event.action === "updated" ? "✓ Updated" : event.action === "skipped" ? "⏭ Skipped" : "✘ Error"; logger_js_1.default.log(`[${event.current}/${event.total}] ${status}: ${event.name}`); } else if (event.type === "complete") { logger_js_1.default.success(`Sync complete: ${event.message ?? "done"}`); } }; const components_js_1 = require("./components.js"); function getErrorMessage(error) { return error instanceof Error ? error.message : String(error); } async function ensureComponentGroupsExist(groupNames, config, options = {}) { try { const existing = await (0, components_js_1.getAllComponentsGroups)(config); const existingNames = new Set((existing ?? []).map((g) => g.name)); for (const groupName of groupNames) { if (!existingNames.has(groupName)) { if (options.dryRun) { logger_js_1.default.warning(`[dry-run] Would create component group '${groupName}'.`); continue; } await (0, components_js_1.createComponentsGroup)(groupName, config); } } } catch (error) { // Log but don't fail - component groups are optional logger_js_1.default.warning(`Could not fetch component groups: ${error instanceof Error ? error.message : String(error)}`); } } function resolveGroupUuid(component, remoteGroups) { if (!component.component_group_name) { return { ...component, component_group_uuid: null }; } const match = remoteGroups.find((g) => g.name === component.component_group_name); if (!match) return { ...component, component_group_uuid: null }; return { ...component, component_group_uuid: match.uuid }; } async function syncComponentsData(args, config) { const { components, presets, ssot, dryRun, onProgress } = args; const progress = onProgress ?? defaultProgress; const result = { created: [], updated: [], skipped: [], errors: [], }; if (dryRun) { logger_js_1.default.warning("[dry-run] Component sync will only read remote data and report planned changes."); } if (ssot) { const existingComponents = await (0, components_js_1.getAllComponents)(config); const existingGroups = await (0, components_js_1.getAllComponentsGroups)(config); if (dryRun) { for (const component of existingComponents ?? []) { logger_js_1.default.warning(`[dry-run] Would remove component '${component.name}'.`); } for (const group of existingGroups ?? []) { logger_js_1.default.warning(`[dry-run] Would remove component group '${group.name}'.`); } } else { const removalTargets = [ ...(existingComponents ?? []).map((component) => ({ type: "component", name: String(component?.name ?? component?.id ?? "unknown"), remove: () => (0, components_js_1.removeComponent)(component, config), })), ...(existingGroups ?? []).map((group) => ({ type: "component group", name: String(group?.name ?? group?.id ?? "unknown"), remove: () => (0, components_js_1.removeComponentGroup)(group, config), })), ]; const removalResults = await Promise.allSettled(removalTargets.map((target) => target.remove())); removalResults.forEach((removalResult, index) => { if (removalResult.status === "fulfilled") return; const target = removalTargets[index]; if (!target) return; const name = `${target.type} '${target.name}'`; const message = getErrorMessage(removalResult.reason); result.skipped.push(name); result.errors.push({ name, message: `SSOT removal failed: ${message}`, }); logger_js_1.default.warning(`Could not remove ${name} during SSOT sync: ${message}`); }); } } const nonEmptyComponents = components.filter((c) => !(0, object_utils_js_1.isObjectEmpty)(c)); const groupsToCheck = (0, array_utils_js_1.uniqueValuesFrom)(nonEmptyComponents .filter((c) => c.component_group_name) .map((c) => c.component_group_name)); await ensureComponentGroupsExist(groupsToCheck, config, { dryRun }); let remoteComponents = []; let remoteGroups = []; try { remoteComponents = ssot && dryRun ? [] : ((await (0, components_js_1.getAllComponents)(config)) ?? []); } catch (error) { logger_js_1.default.warning(`Could not fetch remote components: ${error instanceof Error ? error.message : String(error)}`); } try { remoteGroups = (await (0, components_js_1.getAllComponentsGroups)(config)) ?? []; } catch (error) { logger_js_1.default.warning(`Could not fetch remote groups: ${error instanceof Error ? error.message : String(error)}`); } const componentsToUpdate = []; const componentsToCreate = []; for (const component of nonEmptyComponents) { if (!component?.name) { result.skipped.push("unknown"); continue; } const remote = remoteComponents.find((rc) => rc.name === component.name); if (remote) { componentsToUpdate.push({ id: remote.id, ...component }); } else { componentsToCreate.push(component); } } // Resolve group uuids after ensureComponentGroupsExist const updatePayloads = componentsToUpdate.map((c) => resolveGroupUuid(c, remoteGroups)); const createPayloads = componentsToCreate.map((c) => resolveGroupUuid(c, remoteGroups)); const totalComponents = updatePayloads.length + createPayloads.length; let currentIndex = 0; // Report start progress({ type: "start", total: totalComponents }); // Process updates sequentially for progress reporting for (const component of updatePayloads) { const name = String(component?.name ?? "unknown"); currentIndex++; progress({ type: "progress", current: currentIndex, total: totalComponents, name, action: "updating", }); try { if (dryRun) { logger_js_1.default.warning(`[dry-run] Would update component '${name}'.`); result.updated.push(name); progress({ type: "progress", current: currentIndex, total: totalComponents, name, action: "updated", }); continue; } await (0, components_js_1.updateComponent)(component, presets, config); result.updated.push(name); progress({ type: "progress", current: currentIndex, total: totalComponents, name, action: "updated", }); } catch (error) { result.errors.push({ name, message: getErrorMessage(error), }); progress({ type: "progress", current: currentIndex, total: totalComponents, name, action: "error", message: getErrorMessage(error), }); } } // Process creates sequentially for progress reporting for (const component of createPayloads) { const name = String(component?.name ?? "unknown"); currentIndex++; progress({ type: "progress", current: currentIndex, total: totalComponents, name, action: "creating", }); try { if (dryRun) { logger_js_1.default.warning(`[dry-run] Would create component '${name}'.`); result.created.push(name); progress({ type: "progress", current: currentIndex, total: totalComponents, name, action: "created", }); continue; } await (0, components_js_1.createComponent)(component, presets, config); result.created.push(name); progress({ type: "progress", current: currentIndex, total: totalComponents, name, action: "created", }); } catch (error) { result.errors.push({ name, message: getErrorMessage(error), }); progress({ type: "progress", current: currentIndex, total: totalComponents, name, action: "error", message: getErrorMessage(error), }); } } // Report completion progress({ type: "complete", total: totalComponents, message: `${result.created.length} created, ${result.updated.length} updated, ${result.errors.length} errors`, }); return result; }