UNPKG

@pega/custom-dx-components

Version:

Utility for building custom UI components

988 lines (752 loc) 31.5 kB
import fs from 'fs'; import { promisify } from 'util'; import { join } from 'path'; import inquirer from 'inquirer'; import ncp from 'ncp'; import chalk from 'chalk'; import { Listr } from 'listr2'; import StreamZip from 'node-stream-zip'; import inquirerFuzzyPath from '@pega/inquirer-fuzzy-path'; import { sanitize, checkPathAccess, showVersion, getDirectoryFiles, addDebugLog, checkAccessTokenExpiration, getConfigDefaults, getLibraryBased, getLibraryBasedCL, unZipFromArchive, zipVersionAndArchive, getLibraryArchiveDirectories, getHaveDependencyDifference, hasLibraryAndVersion, cleanUpTemp, hasArchives, restoreFromArchive, forceDefaultsUpdate, getUseInputConfig, getInputConfigForCommand, getPegaServerConfig } from '../../util.js'; import { TASKS_CONFIG_JSON_FILENAME, ARCHIVES_PATH, TEMP_PATH } from '../../constants.js'; import { getFilePathQuestions, getFileNameQuestions, updateTempArchive, updateSavedFilePath, getServerOrDirectoryQuestion, getLibraryArchiveQuestion, getServerLibraryArchives, getServerLibraryArchive } from './helper.js'; import { showCurrentStatus } from '../show-status/index.js'; export const DXCB_CONFIG_INTERNAL_JSON_FILENAME = 'src/dxcb.config.json'; const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); import { convertIntoPascalCase, getComponentDirectoryPath } from '../../util.js'; const copy = promisify(ncp); export const updateConfig = async ( { oldComponentKey, newComponentKey, library, version, targetDirectory, }, options, onlyCompanion = false ) => { addDebugLog("updateConfig", `oldComponentKey: ${oldComponentKey}, newComponentKey: ${newComponentKey}`, ""); let configData = fs.readFileSync(join(targetDirectory, "/config.json"), { encoding: 'utf8' }); configData = configData && JSON.parse(configData); configData.name = newComponentKey; if (configData.componentKey) configData.componentKey = newComponentKey; configData.library = library; configData.version = version; // stringify "4", makes the json string look like JSON in the file, formatted instead of a single line fs.writeFileSync(join(targetDirectory, "/config.json"), JSON.stringify(configData, null, 4), { encoding: 'utf8',flag:'w' }); }; export const updateFile = async ( fileName, oldComponentKeyPC, newComponentKeyPC, targetDirectory, ) => { addDebugLog("updateFile", `fileName: ${fileName}, oldComponentKeyPC: ${oldComponentKeyPC}, newComponentKeyPC: ${newComponentKeyPC}, targetDirectory: ${targetDirectory}`, ""); let fileData = fs.readFileSync(join(targetDirectory, "/", fileName), { encoding: 'utf8' }); if (fileData.indexOf(oldComponentKeyPC) >= 0) { fileData = fileData.replaceAll(oldComponentKeyPC, newComponentKeyPC); fs.writeFileSync(join(targetDirectory, "/", fileName), fileData, { encoding: 'utf8',flag:'w' }); } }; // rename the component export const renameComponent = async ( componentKey, library, version, options ) => { addDebugLog("renameComponent", `library: ${library}, componentKey: ${componentKey}`, ""); const componentName = componentKey.split("_")[2]; const configDef = getConfigDefaults(); const newComponentKey = `${library}_${componentName}`; const oldComponentKey = componentKey; const newComponentKeyPC = convertIntoPascalCase(newComponentKey); const oldComponentKeyPC = convertIntoPascalCase(oldComponentKey); const currentDirectory = await getComponentDirectoryPath(componentKey); const targetDirectory = await getComponentDirectoryPath(newComponentKey); // custom component try { fs.renameSync(currentDirectory, targetDirectory); } catch(err) { console.log(err) } const targetFileList = await getDirectoryFiles(targetDirectory); for (var fileIndex in targetFileList) { const fileName = targetFileList[fileIndex]; if (fileName === "config.json") { await updateConfig( { oldComponentKey, newComponentKey, componentName, library, version, targetDirectory, }, options ); } else { await updateFile( fileName, oldComponentKeyPC, newComponentKeyPC, targetDirectory ); } } // for return newComponentKey; }; export const getPackageOrg = async(packagePath) => { const sPackData = fs.readFileSync(packagePath, { encoding: 'utf8' }); const packData = sPackData && JSON.parse(sPackData); return packData.organization; } export const getLocalConfigData = async(tasksPath) => { const isLibraryBasedCL = getLibraryBasedCL(); let configData = { library: "", version: "", buildVersion: "", devBuild: false}; const sTaskData = fs.readFileSync(tasksPath, { encoding: 'utf8' }); const taskData = sTaskData && JSON.parse(sTaskData); let devBuild = taskData["server-config"].devBuild; // no -dev in PegaInfinity if (isLibraryBasedCL) { devBuild = false; } configData.library = taskData["component"].library; configData.version = taskData["component"].version; configData.devBuild = devBuild; configData.buildVersion = devBuild ? configData.version.concat("-dev") : configData.version; return configData; } export const moveComp = async(componentKey, orgLibName, version, copyShared, options) => { // put lib/version into temp directory const currentDirectory = process.cwd(); const tempDirectory = join(currentDirectory, TEMP_PATH); const archiveDirectory = join (currentDirectory, ARCHIVES_PATH, `${orgLibName}`, `${version}`); const fileName = `${orgLibName}_${version}.arch.zip`; const archFileName = join(archiveDirectory, fileName); await unZipFromArchive(archFileName); // rename the component const newComponentKey = await renameComponent(componentKey, orgLibName, version); const currentCompPath = join(currentDirectory, "src", "components"); const tempCompPath = join(tempDirectory, "src", "components"); const currentComponentsPath = join(currentCompPath, `${newComponentKey}`); const tempDirectoryComponents = join(tempCompPath, `${newComponentKey}`); const currentSharedPath = join(currentCompPath, "shared"); const targetSharedPath = join(tempCompPath, "shared"); // copy component into temp directory fs.cpSync(currentComponentsPath, tempDirectoryComponents, { recursive: true, force: true }); // remove from components fs.rmSync(currentComponentsPath, { recursive: true, force: true, maxRetries: 2 }); // copy shared if (copyShared) { fs.cpSync(currentSharedPath, targetSharedPath, { recursive: true, force: true }); } console.log(`\nCopied ${chalk.green(`${componentKey}`)} to ${chalk.green(`${orgLibName}/${version}`)} as ${chalk.green(`${newComponentKey}`)}\n`); // rearchive temp directory await zipVersionAndArchive(orgLibName, version, tempDirectory); // delete temp directory fs.rmSync(tempDirectory, { recursive: true, force: true, maxRetries: 2 }); } export const importArch = async(filePath, fileName, hadArchive) => { addDebugLog("importArch", `filePath: ${filePath}, fileName: ${fileName}`, ""); const currentDirectory = process.cwd(); const storeDirectory = join (currentDirectory, ARCHIVES_PATH); if (!fs.existsSync(storeDirectory)) { fs.mkdirSync(storeDirectory); } const tempDirectory = join(currentDirectory, TEMP_PATH); const zipFilePath = join(filePath, fileName); // we are going to use node stream zip to list contents of archive first, // before we un archive to a temp directory. If pass, then un archive let passCount = 0; // check to see if have a tasks.config.json and package,json file, if not, archive not good const tempTasksConfig = join(tempDirectory, "tasks.config.json"); const tempPackage = join(tempDirectory, "package.json"); const tasksConfig = "tasks.config.json"; const packageJson = "package.json"; try { const testZip = new StreamZip.async({ file: zipFilePath}); const entries = await testZip.entries(); for (const entry of Object.values(entries)) { if ( entry.name.indexOf(packageJson) >= 0 || entry.name.indexOf(tasksConfig) >= 0) { passCount++; } } // Do not forget to close the file once you're done await testZip.close(); } catch (ex) { passCount = 0; } if (passCount < 2) { console.log(chalk.red.bold(`Zip file: ${fileName} not a compatible DX Component archive.`)); process.exit(1); } await unZipFromArchive(zipFilePath); // need to get info from tasks.config and package.json // we won't rely on name of file, as owner can change it let organization = await getPackageOrg(tempPackage); let localConfig = await getLocalConfigData(tempTasksConfig); let libName = localConfig.library; let orgLibName = `${organization}_${libName}`; const version = localConfig.buildVersion; const devBuild = localConfig.devBuild; const configDef = getConfigDefaults(); if (organization !== configDef.organization) { console.log(`\nImported ${chalk.yellow(`${orgLibName}/${version}`)} is not the same organization as your current organization: ${chalk.yellow(`${configDef.organization}`)}.`); console.log(`\nTo import, the organization of the components will be changed to match your organization.`); console.log(`\n\t>>> If you don't want this to happen, then end and create a new project with an organization`); console.log(`\t of ${chalk.yellow(`${organization}`)} and import there.\n`); const proceedAnswers = await inquirer.prompt([ { name: 'okToContinue', type: 'confirm', message: `Ok to proceed ?`, default: true } ]); if (proceedAnswers.okToContinue) { // check if new version matches existing // -dev is already on version, if present, so "false", for devBuild here const alreadyExists = await hasLibraryAndVersion(libName, version, false); if (alreadyExists) { console.log(`${chalk.yellow(`${configDef.organization}_${libName}/${version}`)} already exists.`); const archDirectories = await getLibraryArchiveDirectories(); const newLibNameAnswers = await inquirer.prompt([ { name: 'libraryName', message: 'Enter new library name', validate: value => { /* value should not be empty It should not have spaces It should not start with a number Only case-insensitive alphanumeric values are allowed */ if (value && !/^\d/.test(value) && value === sanitize(value)) { if (archDirectories && archDirectories.length > 0) { const newOrgLib = `${configDef.organization}_${value}`; if (archDirectories.includes(newOrgLib)) { return `Library ${value} already exists.`; } else { return true; } } else { return true; } } else { return 'Only alphanumeric values are allowed, starting with alphabets, no spaces.'; } } } ]); libName = newLibNameAnswers.libraryName; } // rename orgLibName orgLibName = `${configDef.organization}_${libName}`; // update temp with new info await updateTempArchive(libName, version, devBuild); } else { process.exit(); } } // rearchive temp directory await zipVersionAndArchive(orgLibName, version, tempDirectory); // delete temp directory fs.rmSync(tempDirectory, { recursive: true, force: true, maxRetries: 2 }); if (hadArchive) { if (orgLibName === configDef.currentOrgLib && version === configDef.version) { console.log(`\n${chalk.bold.green(`${fileName}`)} has been imported!`); console.log(`It is the same library/version as your current source.`); console.log(`--> You can retrieve and replace current source via ${chalk.bold.green(`npm run restoreLibVersion`)}.`); console.log("\n"); } else if (orgLibName === configDef.currentOrgLib && version != configDef.version) { console.log(`\n${chalk.bold.green(`${fileName}`)} has been imported!`); console.log(`--> You can switch to ${chalk.bold.green(`${orgLibName}/${version}`)} via ${chalk.bold.green(`npm run switchLibVersion`)}.`); console.log("\n"); } else { console.log(`\n${chalk.bold.green(`${fileName}`)} has been imported!`); console.log(`--> You can switch to ${chalk.bold.green(`${orgLibName}/${version}`)} via ${chalk.bold.green(`npm run switchLib`)}.`); console.log("\n"); } } else { const switchToFileName = `${orgLibName}_${version}.arch.zip`; const tasks = new Listr( [ { title: `Restoring ${orgLibName}/${version}`, task: async () => { await restoreFromArchive(switchToFileName, orgLibName, version); } } ], { concurrent: false, exitOnError: true } ); console.log(`\n${chalk.green(`${fileName}`)} has been imported!`); await tasks.run().catch(err => { console.log(chalk.bold.red(err.toString())); process.exit(1); }); console.log(`${chalk.bold.green(`\nSwitched to ${orgLibName}/${version}!\n`)}`); await forceDefaultsUpdate(); await showCurrentStatus(); if (getHaveDependencyDifference()) { console.log('\n***************************************************************'); console.log(chalk.bold.yellow('Dependencies have changed between versions, recommend updating.')); console.log(`>>> PLEASE run ${chalk.bold.yellow("'npm update'")}.`); console.log('***************************************************************\n'); } } } export const importArchServer = async(filePath, fileName, hadArchive) => { addDebugLog("importArch", `filePath: ${filePath}, fileName: ${fileName}`, ""); const currentDirectory = process.cwd(); const storeDirectory = join (currentDirectory, ARCHIVES_PATH); if (!fs.existsSync(storeDirectory)) { fs.mkdirSync(storeDirectory); } const tempDirectory = join(currentDirectory, TEMP_PATH); const zipFilePath = join(filePath, fileName); // we are going to use node stream zip to list contents of archive first, // before we un archive to a temp directory. If pass, then un archive let passCount = 0; // check to see if have a tasks.config.json and package,json file, if not, archive not good const tempTasksConfig = join(tempDirectory, "tasks.config.json"); const tempPackage = join(tempDirectory, "package.json"); const tasksConfig = "tasks.config.json"; const packageJson = "package.json"; let testZip; try { testZip = new StreamZip.async({ file: zipFilePath}); const entries = await testZip.entries(); for (const entry of Object.values(entries)) { if ( entry.name.indexOf(packageJson) >= 0 || entry.name.indexOf(tasksConfig) >= 0) { passCount++; } } } catch (ex) { passCount = 0; } if (passCount < 2) { console.log(chalk.red.bold(`Zip file: ${fileName} not a compatible DX Component archive.`)); // Do not forget to close the file once you're done await testZip.close(); process.exit(1); } // make directory in temp dir, can NOT be the same directory as the zip file is in const dirName = fileName.replaceAll(".zip", ""); const newDirPath = join(TEMP_PATH, dirName); fs.mkdirSync(newDirPath); const count = await testZip.extract(null, newDirPath); // Do not forget to close the file once you're done await testZip.close(); // we want file contents to be in the TEMP_PATH (copying, but temp directory eventually will be deleted) fs.cpSync(newDirPath, TEMP_PATH, {recursive: true}); // await unZipFromArchive(zipFilePath); // need to get info from tasks.config and package.json // we won't rely on name of file, as owner can change it let organization = await getPackageOrg(tempPackage); let localConfig = await getLocalConfigData(tempTasksConfig); let libName = localConfig.library; let orgLibName = `${organization}_${libName}`; const version = localConfig.buildVersion; const devBuild = localConfig.devBuild; const configDef = getConfigDefaults(); if (organization !== configDef.organization) { console.log(`\nImported ${chalk.yellow(`${orgLibName}/${version}`)} is not the same organization as your current organization: ${chalk.yellow(`${configDef.organization}`)}.`); console.log(`\nTo import, the organization of the components will be changed to match your organization.`); console.log(`\n\t>>> If you don't want this to happen, then end and create a new project with an organization`); console.log(`\t of ${chalk.yellow(`${organization}`)} and import there.\n`); const proceedAnswers = await inquirer.prompt([ { name: 'okToContinue', type: 'confirm', message: `Ok to proceed ?`, default: true } ]); if (proceedAnswers.okToContinue) { // check if new version matches existing // -dev is already on version, if present, so "false", for devBuild here const alreadyExists = await hasLibraryAndVersion(libName, version, false); if (alreadyExists) { console.log(`${chalk.yellow(`${configDef.organization}_${libName}/${version}`)} already exists.`); const archDirectories = await getLibraryArchiveDirectories(); const newLibNameAnswers = await inquirer.prompt([ { name: 'libraryName', message: 'Enter new library name', validate: value => { /* value should not be empty It should not have spaces It should not start with a number Only case-insensitive alphanumeric values are allowed */ if (value && !/^\d/.test(value) && value === sanitize(value)) { if (archDirectories && archDirectories.length > 0) { const newOrgLib = `${configDef.organization}_${value}`; if (archDirectories.includes(newOrgLib)) { return `Library ${value} already exists.`; } else { return true; } } else { return true; } } else { return 'Only alphanumeric values are allowed, starting with alphabets, no spaces.'; } } } ]); libName = newLibNameAnswers.libraryName; } // rename orgLibName orgLibName = `${configDef.organization}_${libName}`; // update temp with new info await updateTempArchive(libName, version, devBuild); } else { process.exit(); } } // rearchive temp directory await zipVersionAndArchive(orgLibName, version, tempDirectory); // delete temp directory fs.rmSync(tempDirectory, { recursive: true, force: true, maxRetries: 2 }); if (hadArchive) { if (orgLibName === configDef.currentOrgLib && version === configDef.version) { console.log(`\n${chalk.bold.green(`${fileName}`)} has been imported!`); console.log(`It is the same library/version as your current source.`); console.log(`--> You can retrieve and replace current source via ${chalk.bold.green(`npm run restoreLibVersion`)}.`); console.log("\n"); } else if (orgLibName === configDef.currentOrgLib && version != configDef.version) { console.log(`\n${chalk.bold.green(`${fileName}`)} has been imported!`); console.log(`--> You can switch to ${chalk.bold.green(`${orgLibName}/${version}`)} via ${chalk.bold.green(`npm run switchLibVersion`)}.`); console.log("\n"); } else { console.log(`\n${chalk.bold.green(`${fileName}`)} has been imported!`); console.log(`--> You can switch to ${chalk.bold.green(`${orgLibName}/${version}`)} via ${chalk.bold.green(`npm run switchLib`)}.`); console.log("\n"); } } else { const switchToFileName = `${orgLibName}_${version}.arch.zip`; const tasks = new Listr( [ { title: `Restoring ${orgLibName}/${version}`, task: async () => { await restoreFromArchive(switchToFileName, orgLibName, version); } } ], { concurrent: false, exitOnError: true } ); console.log(`\n${chalk.green(`${fileName}`)} has been imported!`); await tasks.run().catch(err => { console.log(chalk.bold.red(err.toString())); process.exit(1); }); console.log(`${chalk.bold.green(`\nSwitched to ${orgLibName}/${version}!\n`)}`); await forceDefaultsUpdate(); await showCurrentStatus(); if (getHaveDependencyDifference()) { console.log('\n***************************************************************'); console.log(chalk.bold.yellow('Dependencies have changed between versions, recommend updating.')); console.log(`>>> PLEASE run ${chalk.bold.yellow("'npm update'")}.`); console.log('***************************************************************\n'); } } } export const getZipFileList = async(filePath) => { if (fs.existsSync(filePath)) { return fs .readdirSync(filePath, { withFileTypes: true }) .filter(dirent => !dirent.isDirectory() && dirent.name.match(/.*\.(zip?)/ig)) .map(dirent => dirent.name); } else { console.log(chalk.red.bold(`File path: ${filePath} does not exist or no permissions.`)); process.exit(1); } } function decodeBase64(encodedString) { try { const decodedString = atob(encodedString); return decodedString; } catch (error) { console.error("Error decoding base64 string:", error); return null; } } export const writeZipBuffer = async(fileName, fileBuffer) => { const currentDirectory = process.cwd(); const tempDirectory = join(currentDirectory, TEMP_PATH); const storeDirectory = join (currentDirectory, ARCHIVES_PATH); if (!fs.existsSync(storeDirectory)) { fs.mkdirSync(storeDirectory); } if (!fs.existsSync (tempDirectory)) { fs.mkdirSync(tempDirectory); } const filePath = join(tempDirectory, fileName); fs.writeFileSync(filePath, fileBuffer, 'base64'); } export default async options => { await showVersion(); // await checkLibraryAndArchives(); await checkPathAccess(pegaConfigJsonPath); const isLibraryBased = getLibraryBased(); const isLibraryBasedCL = getLibraryBasedCL(); const useInputConfig = getUseInputConfig(); if (!isLibraryBased) { console.log(`Command only supported for ${chalk.bold.green('library mode')} components.`) process.exit(); } const hasArch = await hasArchives(); await cleanUpTemp(); addDebugLog("importArchive", "", "+"); if (options.params.length >= 6) { const localOrServer = options.params[3]; const filePath = options.params[4]; const fileName = options.params[5]; await importArch( filePath, fileName, hasArch); } else { if (useInputConfig) { const inputConfig = await getInputConfigForCommand("importLibVersion"); if (!inputConfig || Object.keys(inputConfig).length === 0) { console.log(chalk.redBright("Configured for input config, but no input.config.json file found.")); process.exit(1); } const serverOrDirectory = inputConfig.serverOrDirectory; const filePath = inputConfig.fileLocation; const fileName = inputConfig.fileName; const serverAppName = "ComponentLibraryArchive"; const serverArchiveName = inputConfig.serverArchiveName; const serverArchiveType = "zip"; const serverRulesetName = inputConfig.serverRulesetName; const serverRulesetVersion = inputConfig.serverRulesetVersion; console.log(""); if (serverOrDirectory.toLowerCase() === "server") { await checkAccessTokenExpiration(); if (serverArchiveName) { // ALL option const defaultPegaServerConfig = await getPegaServerConfig(); let fileName = `${serverArchiveName}.${serverArchiveType}`; let fileData; console.log(""); const tasks = new Listr( [ { title: 'Fetching archive from server', task: async () => { // get archive fileData = await getServerLibraryArchive(serverAppName, serverArchiveName, serverArchiveType, serverRulesetName, serverRulesetVersion); } }, { title: 'Saving archive in store', task: async () => { await writeZipBuffer(fileName, fileData.pyFileSource); // import archive const currentDirectory = process.cwd(); const tempDirectory = join(currentDirectory, TEMP_PATH); await importArchServer( tempDirectory, fileName, hasArch); } } ], { concurrent: false, exitOnError: true } ); await tasks.run().catch(err => { console.log(chalk.bold.red(err.toString())); process.exit(1); }); } else { // No library with that name console.log(chalk.redBright(`No library archives exist on server.`)); process.exit(); } } else { const tasks = new Listr( [ { title: `Importing archive`, task: async () => { await importArch( filePath, fileName, hasArch); } } ], { concurrent: false, exitOnError: true } ); await tasks.run().catch(err => { console.log(chalk.bold.red(err.toString())); process.exit(1); }); console.log(""); // reapply to save if changed, because imported task.config may have overridden and we want // to keep what was entered as the latest await updateSavedFilePath(filePath); } } else { inquirer.registerPrompt('fuzzypath', inquirerFuzzyPath); let importFrom = "Directory"; if (isLibraryBasedCL) { const localOrServerQuestion = await getServerOrDirectoryQuestion(); const localOrServerAnswer = await inquirer.prompt(localOrServerQuestion); ({importFrom} = localOrServerAnswer); } else { importFrom = "Directory"; } if (importFrom === "Server") { await checkAccessTokenExpiration(); let archiveVersions = []; try { archiveVersions = await getServerLibraryArchives(); } catch (ex) { console.log(chalk.redBright(ex)); process.exit(1); } if (archiveVersions && archiveVersions.pxResults.length > 0) { // ALL option const defaultPegaServerConfig = await getPegaServerConfig(); console.log(`\nRetrieved archives from server ${chalk.bold.green(`${defaultPegaServerConfig.server}`)}.`) // to name value pairs const archiveResults = archiveVersions["pxResults"].map( archive => ( { value: `${archive.pyApplicationName},${archive.pyFileName},${archive.pyFileType},${archive.pyRuleSet},${archive.pyRuleSetVersion}`, name: `${archive.pyLabel}, ${archive.pyRuleSet}:${archive.pyRuleSetVersion}` }) ); const versionQuestions = await getLibraryArchiveQuestion(archiveResults); const versionAnswers = await inquirer.prompt(versionQuestions); const {archiveData} = versionAnswers; if (archiveData != "") { const arArchData = archiveData.split(","); let fileName = `${arArchData[1]}.${arArchData[2]}`; let fileData; console.log(""); const tasks = new Listr( [ { title: 'Fetching archive from server', task: async () => { // get archive fileData = await getServerLibraryArchive(arArchData[0],arArchData[1], arArchData[2], arArchData[3], arArchData[4]); } }, { title: 'Saving archive in store', task: async () => { await writeZipBuffer(fileName, fileData.pyFileSource); // import archive const currentDirectory = process.cwd(); const tempDirectory = join(currentDirectory, TEMP_PATH); await importArchServer( tempDirectory, fileName, hasArch); } } ], { concurrent: false, exitOnError: true } ); await tasks.run().catch(err => { console.log(chalk.bold.red(err.toString())); process.exit(1); }); } } else { // No library with that name console.log(chalk.redBright(`No library archives exist on server.`)); process.exit(); } } else { let filePathQuestions; try { filePathQuestions = await getFilePathQuestions(); } catch (ex) { process.exit(1); } let filePathAnswers; try { filePathAnswers = await inquirer.prompt(filePathQuestions); } catch(ex) { process.exit(1); } const { filePath } = filePathAnswers; const zipFileList = await getZipFileList(filePath); if (!zipFileList || zipFileList.length === 0) { console.log(chalk.red.bold(`No zip files exist at path: ${filePath}`)); process.exit(); } await updateSavedFilePath(filePath); const fileNameQuestions = await getFileNameQuestions(zipFileList); const fileNameAnswers = await inquirer.prompt(fileNameQuestions); const {fileName} = fileNameAnswers; console.log(""); const tasks = new Listr( [ { title: `Importing archive`, task: async () => { await importArch( filePath, fileName, hasArch); } } ], { concurrent: false, exitOnError: true } ); await tasks.run().catch(err => { console.log(chalk.bold.red(err.toString())); process.exit(1); }); console.log(""); // reapply to save if changed, because imported task.config may have overridden and we want // to keep what was entered as the latest await updateSavedFilePath(filePath); } } } addDebugLog("importArchive", "END", "-"); };