@forge/cli

12.23.0-next.7-experimental-44b7a1213.0.0-next.11
out/command-line/controller/module-add-controller.js
+out/command-line/controller/module-add-controller.jsNew file
+165
Index: package/out/command-line/controller/module-add-controller.js
===================================================================
--- package/out/command-line/controller/module-add-controller.js
+++ package/out/command-line/controller/module-add-controller.js
@@ -0,0 +1,165 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.ModuleAddController = exports.MODULE_PRODUCTS = void 0;
+const tslib_1 = require("tslib");
+const path_1 = tslib_1.__importDefault(require("path"));
+const dependencies_merger_1 = require("../../module-add/dependencies-merger");
+const cli_shared_1 = require("@forge/cli-shared");
+const register_app_commands_1 = require("../register-app-commands");
+exports.MODULE_PRODUCTS = [
+    register_app_commands_1.TemplateContext.JIRA,
+    register_app_commands_1.TemplateContext.CONFLUENCE,
+    register_app_commands_1.TemplateContext.JIRA_SERVICE_MANAGEMENT
+];
+class ModuleAddController {
+    moduleView;
+    moduleService;
+    fileService;
+    constructor(moduleView, moduleService, fileService) {
+        this.moduleView = moduleView;
+        this.moduleService = moduleService;
+        this.fileService = fileService;
+    }
+    async run(options) {
+        try {
+            await this.runModuleAdd(options);
+        }
+        finally {
+            this.moduleService.cleanup();
+        }
+    }
+    async runModuleAdd(options) {
+        const selectedTemplate = options.moduleType
+            ? await this.resolveTemplateByModuleType(options.moduleType)
+            : await this.resolveSelectedTemplate(await this.resolveProduct(options));
+        const uiType = options.uiType ?? (await this.resolveUIFramework(selectedTemplate));
+        const downloadedTemplate = await this.moduleService.prepareModuleMetadata(selectedTemplate, uiType);
+        if (!downloadedTemplate) {
+            throw new Error(cli_shared_1.Text.module.add.errorFailedToDownloadTemplate(selectedTemplate.moduleKey));
+        }
+        const depChanges = this.fileService.checkDependencies(downloadedTemplate, options.force === true);
+        const hasConflicts = depChanges.conflicts.length > 0;
+        this.assertNotBlockedByConflicts(depChanges, hasConflicts, './package.json', options);
+        const variables = await this.collectVariables(downloadedTemplate, uiType);
+        const frontendPlan = (0, dependencies_merger_1.planFrontendPackageJson)(process.cwd(), downloadedTemplate, variables, options.force === true);
+        const frontendHasConflicts = frontendPlan.depChanges.conflicts.length > 0;
+        const frontendScope = frontendPlan.frontendPackageJsonPath
+            ? path_1.default.relative(process.cwd(), frontendPlan.frontendPackageJsonPath)
+            : 'frontend package.json';
+        this.assertNotBlockedByConflicts(frontendPlan.depChanges, frontendHasConflicts, frontendScope, options);
+        if (!options.dryRun) {
+            await this.moduleService.downloadModuleBundle(downloadedTemplate, uiType);
+        }
+        const result = await this.fileService.templateMerge(downloadedTemplate, variables, {
+            dryRun: options.dryRun,
+            force: options.force,
+            install: options.install
+        });
+        this.renderRunResult(result, depChanges, hasConflicts, options);
+    }
+    async resolveProduct(options) {
+        const product = options.product ?? (await this.moduleView.promptForList(cli_shared_1.Text.module.add.promptSelectProduct, exports.MODULE_PRODUCTS));
+        if (!exports.MODULE_PRODUCTS.includes(product)) {
+            throw new Error(cli_shared_1.Text.module.add.errorInvalidProduct(product));
+        }
+        return product;
+    }
+    async resolveTemplateByModuleType(moduleType) {
+        for (const product of exports.MODULE_PRODUCTS) {
+            const moduleMap = await this.moduleService.getAvailableModules(product);
+            const template = moduleMap.get(moduleType);
+            if (template) {
+                return template;
+            }
+        }
+        throw new Error(cli_shared_1.Text.module.add.errorFailedToResolveChoice(moduleType));
+    }
+    async resolveSelectedTemplate(product) {
+        const moduleMap = await this.moduleService.getAvailableModules(product);
+        const moduleChoiceMap = this.moduleService.getModuleChoice(moduleMap);
+        const selectedChoice = await this.moduleView.promptForList(cli_shared_1.Text.module.add.promptSelectModule, Array.from(moduleChoiceMap.keys()));
+        const template = moduleChoiceMap.get(selectedChoice);
+        if (!template) {
+            throw new Error(cli_shared_1.Text.module.add.errorFailedToResolveChoice(selectedChoice));
+        }
+        return template;
+    }
+    assertNotBlockedByConflicts(depChanges, hasConflicts, scope, options) {
+        const blocked = hasConflicts && options.force !== true && options.install !== false;
+        if (blocked && !options.dryRun) {
+            this.moduleView.renderDepConflictsBlocked(depChanges, scope);
+            throw new Error(cli_shared_1.Text.module.add.depConflictsSummary(depChanges.conflicts.length));
+        }
+    }
+    renderRunResult(result, depChanges, hasConflicts, options) {
+        if (options.dryRun) {
+            this.moduleView.renderApplyResult(result, depChanges);
+        }
+        if (hasConflicts) {
+            this.renderConflicts(depChanges, './package.json', options);
+        }
+        if (result.frontendDepChanges && result.frontendPackageJsonPath) {
+            const frontendScope = path_1.default.relative(process.cwd(), result.frontendPackageJsonPath);
+            this.renderConflicts(result.frontendDepChanges, frontendScope, options);
+        }
+        for (const w of result.warnings) {
+            this.moduleView.warn(w);
+        }
+        this.moduleView.emptyLine();
+    }
+    renderConflicts(changes, scope, options) {
+        if (options.dryRun && options.force !== true && options.install !== false) {
+            this.moduleView.renderDepConflictsBlocked(changes, scope);
+        }
+        else {
+            this.moduleView.renderDepConflictsResolved(changes, scope);
+        }
+    }
+    async resolveUIFramework(template) {
+        if (!template.variants) {
+            return undefined;
+        }
+        const variants = Object.keys(template.variants);
+        if (variants.length > 1) {
+            return this.moduleView.promptForList(cli_shared_1.Text.module.add.promptSelectUIFramework, variants);
+        }
+        if (variants.length === 1) {
+            return variants[0];
+        }
+        return undefined;
+    }
+    async collectVariables(template, uiFramework) {
+        const variables = {};
+        const variableDefs = this.moduleService.getVariableDefinitions(template, uiFramework);
+        if (variableDefs.length === 0) {
+            return variables;
+        }
+        const { manifestPath, existingKeys } = this.moduleService.loadManifestContext();
+        const fragmentContent = template.cacheDir ? this.moduleService.readManifestFragment(template.cacheDir) : undefined;
+        for (const varDef of variableDefs) {
+            variables[varDef.name] = await this.promptForVariable(varDef, variables, existingKeys, template.moduleKey, fragmentContent, manifestPath);
+        }
+        return variables;
+    }
+    async promptForVariable(varDef, variables, existingKeys, moduleKey, fragmentContent, manifestPath) {
+        while (true) {
+            const effectiveDefault = this.moduleService.computeEffectiveDefault(varDef, variables);
+            const raw = await this.moduleView.promptForText(varDef.prompt, effectiveDefault);
+            const trimmed = (raw ?? '').trim();
+            const value = trimmed !== '' ? trimmed : effectiveDefault || '';
+            const errors = await this.moduleService.validateCandidateValue(varDef, value, existingKeys, moduleKey, fragmentContent, variables, manifestPath);
+            if (errors.length > 0) {
+                for (const err of errors) {
+                    this.moduleView.warn(err);
+                }
+                continue;
+            }
+            if (varDef.name === 'resourceKey' && existingKeys.resourceKeys.has(value)) {
+                this.moduleView.info(cli_shared_1.Text.module.add.infoReusingResource(value));
+            }
+            return value;
+        }
+    }
+}
+exports.ModuleAddController = ModuleAddController;
+//# sourceMappingURL=module-add-controller.js.map
\ No newline at end of file