From 4408633066599c494657b596b0700412ab0a9555 Mon Sep 17 00:00:00 2001 From: Rafa Mel Date: Tue, 30 Apr 2019 02:47:30 +0200 Subject: [PATCH] feat(public/kpo): adds raise --- src/public/kpo/index.ts | 1 + src/public/kpo/raise.ts | 113 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/public/kpo/raise.ts diff --git a/src/public/kpo/index.ts b/src/public/kpo/index.ts index 9c3a9c6..a82f78d 100644 --- a/src/public/kpo/index.ts +++ b/src/public/kpo/index.ts @@ -1,2 +1,3 @@ export { default as run } from './run'; export { default as list } from './list'; +export { default as raise } from './raise'; diff --git a/src/public/kpo/raise.ts b/src/public/kpo/raise.ts new file mode 100644 index 0000000..b4ce95b --- /dev/null +++ b/src/public/kpo/raise.ts @@ -0,0 +1,113 @@ +import fs from 'fs-extra'; +import core, { options as _options } from '~/core'; +import toArgv from 'string-argv'; +import { IOfType } from '~/types'; +import chalk from 'chalk'; +import logger from '~/utils/logger'; +import expose from '~/utils/expose'; +import confirm from '~/utils/confirm'; +import { rejects } from 'errorish'; + +export interface IRaiseOptions { + /** + * Prompt for changes confirmation before performing a write operation + */ + confirm?: boolean; + /** + * Dry run + */ + dry?: boolean; + /** + * Fails if there are any changes to be made on dry mode, or if the user cancels the action when confirmation is required + */ + fail?: boolean; +} + +export default expose(raise); + +/** + * Raises *kpo* tasks to the `package.json` in the project context. + * It is an *exposed* function: call `raise.fn()`, which takes the same arguments, in order to execute on call. + * @returns An asynchronous function -hence, calling `raise` won't have any effect until the returned function is called. + */ +function raise(options: IRaiseOptions = {}): () => Promise { + return async () => { + if (options.confirm && options.dry) { + throw Error(`raise can't be run with both confirm and dry options`); + } + if (options.fail && (!options.confirm && !options.dry)) { + throw Error( + `raise can't be run in fail mode without confirm or dry options` + ); + } + + const paths = await core.paths(); + const { pkg } = await core.load(); + + if (!paths.kpo) throw Error(`No kpo scripts found`); + if (!paths.pkg || !pkg) throw Error(`No package.json found`); + + const tasks = await core.tasks(); + const taskNames = (tasks.kpo || []) + .filter((task) => !task.hidden) + .map((task) => task.path); + + const scripts: IOfType = pkg.scripts || {}; + + const selected = Object.keys(scripts).filter((key) => { + const value = scripts[key]; + let argv = toArgv(value); + if (argv.shift() !== 'kpo') return false; + if (argv[0] === ':run') argv.shift(); + return ( + argv.length === 1 && + argv[0][0] !== ':' && + argv[0][0] !== '@' && + argv[0] === key + ); + }); + const nonSelected = Object.keys(scripts).filter( + (key) => !selected.includes(key) + ); + + const toRemove = selected.filter((key) => !taskNames.includes(key)); + const toAdd = taskNames.filter((key) => !selected.includes(key)); + + let msg = chalk.bold.green('No pending scripts changes'); + if (toRemove.length || toAdd.length) { + msg = toRemove.length + ? chalk.bold.red('Scripts to remove: ') + toRemove.join(', ') + : chalk.bold.green('No scripts to remove'); + msg += + '\n' + + (toAdd.length + ? chalk.bold.yellow('Scripts to add: ') + toAdd.join(', ') + : chalk.bold.green('No scripts to add')); + } + + // eslint-disable-next-line no-console + (options.confirm || options.dry ? console.log : logger.info)(msg); + + if (!toRemove.length && !toAdd.length) return; + if (options.dry) { + if (options.fail) throw Error(`There are pending scripts changes`); + return; + } + + if (!(await confirm('Confirm?', options))) return; + + pkg.scripts = { + ...nonSelected.reduce((acc: IOfType, key) => { + acc[key] = scripts[key]; + return acc; + }, {}), + ...taskNames.reduce((acc: IOfType, key) => { + acc[key] = `kpo ${key}`; + return acc; + }, {}) + }; + + await fs.writeFile(paths.pkg, JSON.stringify(pkg, null, 2)).catch(rejects); + _options.forceUpdate(); + }; +}