-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
114 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export { default as run } from './run'; | ||
export { default as list } from './list'; | ||
export { default as raise } from './raise'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<void> { | ||
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<string> = 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<string>, key) => { | ||
acc[key] = scripts[key]; | ||
return acc; | ||
}, {}), | ||
...taskNames.reduce((acc: IOfType<string>, key) => { | ||
acc[key] = `kpo ${key}`; | ||
return acc; | ||
}, {}) | ||
}; | ||
|
||
await fs.writeFile(paths.pkg, JSON.stringify(pkg, null, 2)).catch(rejects); | ||
_options.forceUpdate(); | ||
}; | ||
} |