UNPKG

eddi-cli

Version:

Command line interface to make project-based editing of EDDI personalities possible.

238 lines (206 loc) 8.9 kB
const path = require('path'); const fs = require('fs/promises'); const inquirer = require('inquirer'); const askQuestions = require('../lib/askQuestions'); const runSafeCommand = require('../lib/util/runSafeCommand'); const getEddiPersonalityOption = require('../lib/options/getEddiPersonalityOption'); const getLocalPersonalityForEddiOption = require('../lib/options/getLocalPersonalityForEddiOption'); const normalizePersonality = require('../lib/options/normalizePersonality'); const readJson = require('../lib/util/readJson'); const writeJson = require('../lib/util/writeJson'); const personalityToFiles = require('../lib/personality/personalityToFiles'); const diffPersonality = require('../lib/personality/diffPersonality'); const writeTemplate = require('../lib/template/writeTemplate'); const log = require('../lib/log'); const { CONFIG_FILENAME, PERSONALITIES_FOLDER, PERSONALITY_FILENAME } = require('../lib/constants'); exports.command = 'sync'; exports.describe = 'Synchronize changes made in EDDI with the local files'; async function getOpts() { const personality = await getEddiPersonalityOption(); const localPersonality = await getLocalPersonalityForEddiOption(personality); return { personality: { ...personality, desc: 'The personality to sync', group: 'Sync options:', question: 'Which personality do you want to sync?', }, localPersonality: { ...localPersonality, desc: 'The local personality to sync with', group: 'Sync options:', question: 'With which local personality do you want to sync?', }, }; } exports.builder = async function (yargs) { const opts = await getOpts(); return yargs.options(opts); }; function createDiffQuestion(diff) { const question = { name: diff.name, type: 'confirm', default: true, }; const msg = 'Template ' + log.c.em(diff.name) + ' was'; if (diff.isUnique) { if (diff.newer === 'eddi') { question.message = msg + ' added in ' + log.c.em('EDDI') + '. Do you want to ' + log.c.success('add') + ' it to ' + log.c.em('local') + '?'; } else { question.message = msg + ' added in ' + log.c.em('local') +'. Do you want to ' + log.c.error('remove') + ' it?'; question.default = false; } } else { if (diff.newer === 'eddi') { question.message = msg + ' last modified in ' + log.c.em('EDDI') + '. Do you want to use ' + log.c.em('EDDI') + "'s version?"; } else { question.message = msg + ' last modified in ' + log.c.em('local') + '. Do you want to ' + log.c.success('keep') + ' using the ' + log.c.em('local') + ' version?'; } } return question; } async function sync(argv) { const opts = await getOpts(); const args = await askQuestions(argv, opts); args.personality = await normalizePersonality(args.personality, PERSONALITIES_FOLDER); args.localPersonality = await normalizePersonality(args.localPersonality); log(); log.options(args, opts); const localPersonalityDir = args.localPersonality; const rootFolder = (localPersonalityDir) ? path.resolve(args.localPersonality, '..') : process.cwd(); let configOpts; try { configOpts = await readJson(path.join(rootFolder, CONFIG_FILENAME)); } catch (e) { if (e.code === 'ENOENT') { log.error('The sync command requires the information stored in ".eddi-cli-config.json". ' + `You're using \`eddi-cli\` in folder "${process.cwd()}", which doesn't have that file. ` + "Please make sure you're using `eddi-cli` in the correct folder. If you haven't yet initialized, " + 'please use `npx eddi-cli init` to start an EDDI project folder with all the necessary setup included.'); throw new Error("Couldn't locate ./eddi-cli-config.json"); } throw e; } const eddiPersonality = await readJson(args.personality); if (localPersonalityDir === null) { log.warn('It looks like no ' + log.c.em('local') + ' personalities matches the selected ' + log.c.em('EDDI') + ' personality.'); const answers = await inquirer.prompt([{ name: 'newLocal', type: 'confirm', default: true, message: 'Do you want to create a new local personality for ' + log.c.em(path.basename(args.personality)) + '?', }]); if (answers.newLocal) { log(); log('> Writing ' + log.c.em(eddiPersonality.name) + '.'); eddiPersonality.filePath = args.personality; await personalityToFiles(eddiPersonality, { ext: configOpts.extension, templateSettings: configOpts.templateSettings, }); } log(); log('DONE'); return; } log('Checking differences between local personality ' + log.c.em(path.basename(localPersonalityDir)) + " and EDDI's " + log.c.em(path.basename(args.personality)) + '.'); log(); const diffMap = await diffPersonality(args.personality, localPersonalityDir); const syncQuestions = Object.values(diffMap).reduce((acc, diff) => { if (diff.isUnique || diff.isDifferent) { acc.push(createDiffQuestion(diff)); } return acc; }, []); if (!syncQuestions.length) { log.success("No differences found, so there's nothing to synchronize."); log(); log('DONE'); return; } log.warn('When the question states that the template was last modified in ' + log.c.em('EDDI') + ', it means that the EDDI personality file was modified after the ' + log.c.em('local') + ' version was modified.', "Since EDDI saves all templates in one go, `eddi-cli` can't judge which version is newer."); log(); const answers = await inquirer.prompt(syncQuestions); const personalityFilePath = path.join(localPersonalityDir, PERSONALITY_FILENAME); const personality = await readJson(personalityFilePath); log(); log('Starting to sync based on the answers given.'); log.verbose(); let madeAnyChanges = false; let shouldUpdatePersonality = false; for (const [key, answer] of Object.entries(answers)) { const diff = diffMap[key]; if (diff.isUnique) { if (diff.newer === 'eddi') { if (answer === true) { log('> Adding EDDI template ' + log.c.em(diff.name) + ' to the local personality.'); personality.scripts[key] = await writeTemplate(personality, eddiPersonality.scripts[key], localPersonalityDir, { ext: configOpts.extension, templateSettings: configOpts.templateSettings, personalityDir: localPersonalityDir, }); shouldUpdatePersonality = true; madeAnyChanges = true; } else { log.verbose(`> Opted to skip adding EDDI template "${diff.name}" to the local personality.`) } } else { if (answer === true) { log('> Deleting local template ' + log.c.em(diff.name) + '.') const templatePath = path.join(localPersonalityDir, personality.scripts[key].script); delete personality.scripts[key]; await fs.rm(templatePath); shouldUpdatePersonality = true; madeAnyChanges = true; } else { log.verbose(`> Opted to skip deleting local template "${diff.name}".`); } } } else { if ((diff.newer === 'eddi' && answer === true) || (diff.newer === 'local' && answer === false)) { log('> Overwriting content of local template ' + log.c.em(diff.name) + ' with the ' + log.c.em('EDDI') + ' version.'); const templatePath = path.join(localPersonalityDir, personality.scripts[key].script); const templateDir = path.dirname(templatePath); await writeTemplate(personality, eddiPersonality.scripts[key], templateDir, { ext: configOpts.extension, templateSettings: configOpts.templateSettings, personalityDir: localPersonalityDir, }); madeAnyChanges = true; } else { log.verbose(`> Opted to skip overwriting content of local template "${diff.name}" with EDDI's version.`) } } } log(); if (shouldUpdatePersonality) { log('Updating personality ' + log.c.em(path.basename(args.personality)) + ' with all the changes.'); await writeJson(personalityFilePath, personality); } else { log.verbose(`No additions or deletions, so skipped updating personality "${path.basename(args.personality)}".`) } if (!madeAnyChanges) { log.warn('The given answers resulted in nothing being changes.'); log(log.c.gray('You can use `npx eddi-cli diff` to get information about differences.')); } log(); log('DONE'); } exports.handler = async function syncCommand(argv) { await runSafeCommand(sync, argv); }