Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(public/fs): allows all fs functions to take sources arrays; unif…
…ies behavior
- Loading branch information
Showing
21 changed files
with
545 additions
and
417 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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,76 @@ | ||
import path from 'path'; | ||
import fs from 'fs-extra'; | ||
import { absolute, exists } from '~/utils/file'; | ||
import { IFsUpdateOptions, TCopyFilterFn } from '../types'; | ||
import confirm from '~/utils/confirm'; | ||
import logger from '~/utils/logger'; | ||
import { open } from '~/utils/errors'; | ||
|
||
// TODO allow to take an option to duplicate folder structure on dest from a base + don't allow it when src is upwards instead of nested in that folder | ||
|
||
export default async function copy( | ||
src: string | string[], | ||
dest: string, | ||
options: IFsUpdateOptions = {}, | ||
filter: TCopyFilterFn = () => true | ||
): Promise<void> { | ||
options = Object.assign({ overwrite: true }, options); | ||
|
||
if (Array.isArray(src)) { | ||
// Check dest is a folder | ||
if (await exists(dest)) { | ||
const stat = await fs.stat(dest); | ||
if (!stat.isDirectory()) { | ||
throw Error('Destination must be a folder for an array of sources'); | ||
} | ||
} | ||
for (let source of src) { | ||
await each( | ||
source, | ||
path.join(dest, path.parse(source).base), | ||
options, | ||
filter | ||
); | ||
} | ||
} else { | ||
await each(src, dest, options, filter); | ||
} | ||
} | ||
|
||
export async function each( | ||
src: string, | ||
dest: string, | ||
options: IFsUpdateOptions, | ||
filter: TCopyFilterFn | ||
): Promise<void> { | ||
const cwd = process.cwd(); | ||
src = absolute({ path: src, cwd }); | ||
dest = absolute({ path: dest, cwd }); | ||
|
||
const relatives = { | ||
src: './' + path.relative(cwd, src), | ||
dest: './' + path.relative(cwd, dest) | ||
}; | ||
|
||
const srcExist = await exists(src, { fail: options.fail }); | ||
if (!srcExist) { | ||
logger.info(`Copy skipped: "${relatives.src}" to "${relatives.dest}"`); | ||
return; | ||
} | ||
|
||
const msg = `Copy "${relatives.src}" to "${relatives.dest}"?`; | ||
if (!(await confirm(msg, options))) return; | ||
|
||
await fs.copy(src, dest, { | ||
overwrite: options.overwrite, | ||
errorOnExist: options.fail, | ||
async filter(src: string, dest: string): Promise<boolean> { | ||
try { | ||
return await filter(src, dest); | ||
} catch (err) { | ||
throw open(err); | ||
} | ||
} | ||
}); | ||
logger.info(`Copied: "${relatives.src}" to "${relatives.dest}"`); | ||
} |
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,39 @@ | ||
import { TSource, IFsUpdateOptions, TCopyFilterFn } from '../types'; | ||
import expose, { TExposedOverload } from '~/utils/expose'; | ||
import trunk from './copy'; | ||
|
||
export default expose(copy) as TExposedOverload< | ||
typeof copy, | ||
| [TSource, string] | ||
| [TSource, string, IFsUpdateOptions] | ||
| [TSource, string, TCopyFilterFn] | ||
| [TSource, string, IFsUpdateOptions | undefined, TCopyFilterFn] | ||
>; | ||
|
||
function copy( | ||
src: TSource, | ||
dest: string, | ||
filter?: TCopyFilterFn | ||
): () => Promise<void>; | ||
function copy( | ||
src: TSource, | ||
dest: string, | ||
options?: IFsUpdateOptions, | ||
filter?: TCopyFilterFn | ||
): () => Promise<void>; | ||
/** | ||
* Recursive copy. If an array of paths is passed as `src`, `dest` will be expected to be a directory. | ||
* It is an *exposed* function: call `copy.fn()`, which takes the same arguments, in order to execute on call. | ||
* @returns An asynchronous function -hence, calling `copy` won't have any effect until the returned function is called. | ||
*/ | ||
function copy(src: TSource, dest: string, ...args: any[]): () => Promise<void> { | ||
return async () => { | ||
const hasOptions = typeof args[0] !== 'function'; | ||
return trunk( | ||
typeof src === 'function' ? await src() : await src, | ||
dest, | ||
hasOptions ? args[0] : undefined, | ||
hasOptions ? args[1] : args[0] | ||
); | ||
}; | ||
} |
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
This file was deleted.
Oops, something went wrong.
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,24 @@ | ||
import expose from '~/utils/expose'; | ||
import trunk from './mkdir'; | ||
import { TSource, IFsCreateDeleteOptions } 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 `IFsCreateDeleteOptions` object. | ||
* @returns An asynchronous function -hence, calling `mkdir` won't have any effect until the returned function is called. | ||
*/ | ||
function mkdir( | ||
paths: TSource, | ||
options?: IFsCreateDeleteOptions | ||
): () => Promise<void> { | ||
return async () => { | ||
return trunk( | ||
typeof paths === 'function' ? await paths() : await paths, | ||
options | ||
); | ||
}; | ||
} |
Oops, something went wrong.