@sprucelabs/spruce-cli
Version: 
Command line interface for building Spruce skills.
213 lines • 10.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils");
const uniq_1 = __importDefault(require("lodash/uniq"));
const syncSchemasOptions_schema_1 = __importDefault(require("./../../../.spruce/schemas/spruceCli/v2020_07_22/syncSchemasOptions.schema"));
const SpruceError_1 = __importDefault(require("../../../errors/SpruceError"));
const SchemaTemplateItemBuilder_1 = __importDefault(require("../../../templateItemBuilders/SchemaTemplateItemBuilder"));
const action_utility_1 = __importDefault(require("../../../utilities/action.utility"));
const AbstractAction_1 = __importDefault(require("../../AbstractAction"));
const schemaDisk_utility_1 = __importDefault(require("../utilities/schemaDisk.utility"));
const ValueTypeBuilder_1 = __importDefault(require("../ValueTypeBuilder"));
class SyncAction extends AbstractAction_1.default {
    optionsSchema = syncSchemasOptions_schema_1.default;
    commandAliases = ['sync.schemas'];
    invocationMessage = 'Building schemas and generating types... 📃';
    schemaWriter = this.Writer('schema');
    schemaStore = this.Store('schema');
    async execute(options) {
        const normalizedOptions = this.validateAndNormalizeOptions(options);
        const isInCoreSchemasModule = this.Service('pkg').get('name') ===
            '@sprucelabs/spruce-core-schemas';
        let { schemaTypesDestinationDirOrFile, fieldTypesDestinationDir, schemaLookupDir, addonsLookupDir, shouldEnableVersioning, globalSchemaNamespace, shouldFetchRemoteSchemas, shouldGenerateCoreSchemaTypes = isInCoreSchemasModule, shouldFetchLocalSchemas, generateFieldTypes, generateStandaloneTypesFile, deleteDestinationDirIfNoSchemas, shouldFetchCoreSchemas, registerBuiltSchemas, syncingMessage, deleteOrphanedSchemas, moduleToImportFromWhenRemote, shouldInstallMissingDependencies, } = normalizedOptions;
        this.ui.startLoading('Loading details about your skill... 🧐');
        let localNamespace = await this.Store('skill').loadCurrentSkillsNamespace();
        let shouldImportCoreSchemas = true;
        if (shouldGenerateCoreSchemaTypes) {
            shouldFetchRemoteSchemas = false;
            shouldFetchLocalSchemas = true;
            shouldFetchCoreSchemas = false;
            registerBuiltSchemas = true;
            generateStandaloneTypesFile = true;
            shouldImportCoreSchemas = false;
            localNamespace = spruce_skill_utils_1.CORE_NAMESPACE;
        }
        let coreSyncResults;
        const { resolvedFieldTypesDestination, resolvedSchemaTypesDestinationDirOrFile, resolvedSchemaTypesDestination, } = schemaDisk_utility_1.default.resolveTypeFilePaths({
            cwd: this.cwd,
            generateStandaloneTypesFile,
            schemaTypesDestinationDirOrFile,
            fieldTypesDestinationDir,
        });
        this.ui.startLoading('Generating field types...');
        const { fieldTemplateItems, fieldErrors, generateFieldFiles } = await this.generateFieldTemplateItems({
            addonsLookupDir,
            shouldGenerateFieldTypes: generateFieldTypes,
            resolvedFieldTypesDestination,
        });
        this.ui.startLoading(syncingMessage);
        const schemaErrors = [];
        let schemaTemplateItems;
        let typeResults = [];
        try {
            const templateResults = await this.generateSchemaTemplateItems({
                schemaLookupDir,
                shouldFetchLocalSchemas,
                moduleToImportFromWhenRemote,
                resolvedSchemaTypesDestinationDirOrFile,
                shouldEnableVersioning,
                shouldFetchRemoteSchemas,
                shouldFetchCoreSchemas,
                localNamespace,
            });
            schemaErrors.push(...templateResults.schemaErrors);
            schemaTemplateItems = templateResults.schemaTemplateItems;
        }
        catch (err) {
            schemaErrors.push(err);
        }
        if (schemaErrors.length === 0 && schemaTemplateItems) {
            if (deleteDestinationDirIfNoSchemas &&
                schemaTemplateItems.length === 0) {
                spruce_skill_utils_1.diskUtil.deleteDir(resolvedSchemaTypesDestinationDirOrFile);
                return {};
            }
            if (deleteOrphanedSchemas) {
                this.ui.startLoading('Identifying orphaned schemas...');
                await schemaDisk_utility_1.default.deleteOrphanedSchemas(resolvedSchemaTypesDestinationDirOrFile, schemaTemplateItems);
            }
            await this.optionallyInstallRemoteModules(schemaTemplateItems, shouldInstallMissingDependencies);
            let valueTypes;
            try {
                valueTypes = await this.generateValueTypes({
                    resolvedDestination: resolvedFieldTypesDestination,
                    fieldTemplateItems,
                    schemaTemplateItems,
                    globalSchemaNamespace: globalSchemaNamespace ?? undefined,
                });
            }
            catch (err) {
                schemaErrors.push(err);
            }
            if (valueTypes) {
                try {
                    this.ui.startLoading('Determining what changed... ⚡️');
                    typeResults = await this.schemaWriter.writeSchemasAndTypes(resolvedSchemaTypesDestination, {
                        registerBuiltSchemas,
                        fieldTemplateItems,
                        schemaTemplateItems,
                        shouldImportCoreSchemas,
                        valueTypes,
                        globalSchemaNamespace: globalSchemaNamespace ?? undefined,
                        typesTemplate: generateStandaloneTypesFile
                            ? 'schema/core.schemas.types.ts.hbs'
                            : undefined,
                    });
                }
                catch (err) {
                    schemaErrors.push(err);
                }
            }
        }
        const p = resolvedSchemaTypesDestination;
        spruce_skill_utils_1.diskUtil.deleteEmptyDirs(spruce_skill_utils_1.diskUtil.isDir(p) ? p : path_1.default.dirname(p));
        this.ui.stopLoading();
        const errors = [...schemaErrors, ...fieldErrors];
        return action_utility_1.default.mergeActionResults(coreSyncResults || {}, {
            files: [...typeResults, ...generateFieldFiles],
            errors: errors.length > 0 ? errors : undefined,
            meta: {
                schemaTemplateItems,
                fieldTemplateItems,
            },
        });
    }
    async optionallyInstallRemoteModules(schemaTemplateItems, forceInstall) {
        const modules = (0, uniq_1.default)(schemaTemplateItems
            .map((item) => item.importFrom)
            .filter((i) => !!i));
        const notInstalled = [];
        const pkg = this.Service('pkg');
        for (const m of modules) {
            if (!pkg.isInstalled(m) && notInstalled.indexOf(m) === -1) {
                notInstalled.push(m);
            }
        }
        if (notInstalled.length > 0) {
            if (!forceInstall) {
                this.ui.stopLoading();
                this.ui.renderSection({
                    headline: `Missing ${notInstalled.length} module${notInstalled.length === 1 ? '' : 's'}`,
                    lines: [
                        `Looks like I need to install the following modules to continue to sync schemas:`,
                        '',
                        ...notInstalled,
                    ],
                });
                const confirm = await this.ui.confirm('Should we do that now?');
                if (!confirm) {
                    throw new SpruceError_1.default({
                        code: 'ACTION_CANCELLED',
                        friendlyMessage: `I can't sync schemas because of the missing modules.`,
                    });
                }
            }
            this.ui.startLoading(`Installing ${notInstalled.length} missing module${notInstalled.length === 1 ? '' : 's...'}`);
            const pkg = this.Service('pkg');
            await pkg.install(notInstalled);
            this.ui.stopLoading();
        }
    }
    async generateSchemaTemplateItems(options) {
        const { schemaLookupDir, resolvedSchemaTypesDestinationDirOrFile, shouldEnableVersioning, shouldFetchRemoteSchemas, shouldFetchCoreSchemas, shouldFetchLocalSchemas, localNamespace, moduleToImportFromWhenRemote, } = options;
        this.ui.startLoading('Loading builders...');
        const { schemasByNamespace, errors: schemaErrors } = await this.schemaStore.fetchSchemas({
            localSchemaLookupDir: schemaLookupDir,
            shouldFetchLocalSchemas,
            shouldFetchRemoteSchemas,
            shouldEnableVersioning,
            moduleToImportFromWhenRemote,
            localNamespace,
            shouldFetchCoreSchemas,
            didUpdateHandler: (message) => {
                this.ui.startLoading(message);
            },
        });
        const hashSpruceDestination = resolvedSchemaTypesDestinationDirOrFile.replace(spruce_skill_utils_1.diskUtil.resolveHashSprucePath(this.cwd), '#spruce');
        let total = 0;
        let totalNamespaces = 0;
        for (const namespace of Object.keys(schemasByNamespace)) {
            totalNamespaces++;
            total += schemasByNamespace[namespace].length;
        }
        this.ui.startLoading(`Building ${total} schemas from ${totalNamespaces} namespaces.`);
        const schemaTemplateItemBuilder = new SchemaTemplateItemBuilder_1.default(localNamespace);
        const schemaTemplateItems = schemaTemplateItemBuilder.buildTemplateItems(schemasByNamespace, hashSpruceDestination);
        return { schemaTemplateItems, schemaErrors };
    }
    async generateFieldTemplateItems(options) {
        const { addonsLookupDir, shouldGenerateFieldTypes: generateFieldTypes, resolvedFieldTypesDestination, } = options;
        const action = this.Action('schema', 'syncFields');
        const results = await action.execute({
            fieldTypesDestinationDir: resolvedFieldTypesDestination,
            addonsLookupDir,
            generateFieldTypes,
        });
        return {
            generateFieldFiles: results.files ?? [],
            fieldTemplateItems: results.meta?.fieldTemplateItems ?? [],
            fieldErrors: results.errors ?? [],
        };
    }
    async generateValueTypes(options) {
        this.ui.startLoading('Generating value types...');
        const builder = new ValueTypeBuilder_1.default(this.schemaWriter, this.Service('import'));
        return builder.generateValueTypes(options);
    }
}
exports.default = SyncAction;
//# sourceMappingURL=SyncAction.js.map