UNPKG

@pega/custom-dx-components

Version:

Utility for building custom UI components

886 lines (724 loc) 30.6 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?)/gi)) .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', '-'); };