From 6109bf8cb8b98a9fbb2f6070d5c07b4554c27638 Mon Sep 17 00:00:00 2001 From: Rafa Mel Date: Sun, 28 Apr 2019 10:08:02 +0200 Subject: [PATCH] feat(exposed/fs, exposed/tags): adds mkdir; uses mkdir on ensure --- src/exposed/fs/index.ts | 3 +- src/exposed/fs/mkdir.ts | 79 ++++++++++++++++++++++++++++++++++++++ src/exposed/tags/ensure.ts | 13 ++----- 3 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 src/exposed/fs/mkdir.ts diff --git a/src/exposed/fs/index.ts b/src/exposed/fs/index.ts index c560e76..f086f31 100644 --- a/src/exposed/fs/index.ts +++ b/src/exposed/fs/index.ts @@ -1,6 +1,7 @@ export { default as json } from './json'; export { default as remove } from './remove'; +export { default as mkdir } from './mkdir'; export { default as rw } from './rw'; export { default as write } from './write'; -// TODO: copy, mkdir, move +// TODO: copy, move diff --git a/src/exposed/fs/mkdir.ts b/src/exposed/fs/mkdir.ts new file mode 100644 index 0000000..cd27ae4 --- /dev/null +++ b/src/exposed/fs/mkdir.ts @@ -0,0 +1,79 @@ +import path from 'path'; +import fs from 'fs-extra'; +import { rejects } from 'errorish'; +import core from '~/core'; +import { absolute, exists } from '~/utils/file'; +import confirm from '../prompts/confirm'; +import { parallel } from 'promist'; +import logger from '~/utils/logger'; +import chalk from 'chalk'; +import expose from '~/utils/expose'; +import { IFsReadOptions } from './types'; + +export default expose(mkdir); +/** + * Deep creates a directory or an array of them. + * It is an *exposed* function: call `mkdir.fn()`, which takes the same arguments, in order to execute on call. + * @param paths a path for a directory, or an array of them. + * @param options an `IFsReadOptions` object. In this case, if `fail` is true, `mkdir` will fail if one of the directories already exists. + * @returns An asynchronous function -hence, calling `mkdir` won't have any effect until the returned function is called. + */ +function mkdir( + paths: string | string[], + options: IFsReadOptions = {} +): () => Promise { + return async () => { + options = Object.assign({ confirm: false, fail: true }, options); + + const cwd = await core.cwd(); + paths = Array.isArray(paths) ? paths : [paths]; + paths = paths.map((path) => absolute({ path, cwd })); + + const existingPaths = await parallel.filter(paths, (path) => exists(path)); + const nonExistingPaths = paths.filter( + (path) => !existingPaths.includes(path) + ); + const relatives = { + existing: existingPaths.map((x) => path.relative(cwd, x)), + nonExisting: nonExistingPaths.map((x) => path.relative(cwd, x)) + }; + + if (options.fail && existingPaths.length) { + throw Error(`Directory already exists: ${relatives.existing[0]}`); + } + + // eslint-disable-next-line no-console + (options.confirm ? console.log : logger.debug)( + chalk.bold.yellow( + relatives.existing.length + ? 'Directories to create' + : 'No directories to create' + ) + + (relatives.existing.length + ? `\n Existing paths: "${relatives.existing.join('", "')}"` + : '') + + (relatives.nonExisting.length + ? `\n Non existing paths: "${relatives.nonExisting.join('", "')}"` + : '') + ); + + if (!nonExistingPaths.length) return; + if (options.confirm) { + const action = await confirm.fn({ no: false }).then((x) => x !== false); + + if (!action) { + if (options.fail) throw Error(`Cancelled by user`); + else return; + } + } + + await parallel.each(nonExistingPaths, async (absolute, i) => { + await fs.ensureDir(absolute).catch(rejects); + + const relative = relatives.nonExisting[i]; + logger.debug(`Created: ${relative}`); + }); + + logger.info(`Created: "${relatives.nonExisting.join('", "')}"`); + }; +} diff --git a/src/exposed/tags/ensure.ts b/src/exposed/tags/ensure.ts index b6c0d4b..3aafd1f 100644 --- a/src/exposed/tags/ensure.ts +++ b/src/exposed/tags/ensure.ts @@ -1,9 +1,6 @@ -import fs from 'fs-extra'; -import core from '~/core'; import asTag from '~/utils/as-tag'; -import { rejects } from 'errorish'; import expose, { TExposedOverload } from '~/utils/expose'; -import { absolute } from '~/utils/file'; +import mkdir from '../fs/mkdir'; export default expose(ensure) as TExposedOverload< typeof ensure, @@ -22,11 +19,7 @@ function ensure( */ function ensure(...args: any[]): () => Promise { return async () => { - const directory = absolute({ - path: asTag(args.shift(), ...args), - cwd: await core.cwd() - }); - - await fs.ensureDir(directory).catch(rejects); + const path = asTag(args.shift(), ...args); + return mkdir.fn(path, { confirm: false, fail: false }); }; }