Skip to content

Commit

Permalink
feat(public/fs): allows all fs functions to take sources arrays; unif…
Browse files Browse the repository at this point in the history
…ies behavior
  • Loading branch information
rafamel committed May 20, 2019
1 parent cf99127 commit d0f6536
Show file tree
Hide file tree
Showing 21 changed files with 545 additions and 417 deletions.
103 changes: 0 additions & 103 deletions src/public/fs/copy.ts

This file was deleted.

76 changes: 76 additions & 0 deletions src/public/fs/copy/copy.ts
@@ -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}"`);
}
39 changes: 39 additions & 0 deletions src/public/fs/copy/index.ts
@@ -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]
);
};
}
9 changes: 5 additions & 4 deletions src/public/fs/json.ts
@@ -1,7 +1,7 @@
import { IOfType } from '~/types';
import expose from '~/utils/expose';
import rw from './rw';
import { IFsUpdateOptions } from './types';
import { IFsUpdateOptions, TContentFn, TSource } from './types';

export default expose(json);

Expand All @@ -11,14 +11,15 @@ export default expose(json);
* @returns An asynchronous function -hence, calling `json` won't have any effect until the returned function is called.
*/
function json(
file: string,
file: TSource,
fn: (
file: string,
json?: IOfType<any>
) => IOfType<any> | void | Promise<IOfType<any> | void>,
options: IFsUpdateOptions = {}
options?: IFsUpdateOptions
): () => Promise<void> {
return async () => {
const _fn = async (raw?: string): Promise<string | undefined> => {
const _fn: TContentFn = async (file, raw) => {
const json = await fn(raw ? JSON.parse(raw) : undefined);
return json ? JSON.stringify(json, null, 2) : undefined;
};
Expand Down
75 changes: 0 additions & 75 deletions src/public/fs/mkdir.ts

This file was deleted.

24 changes: 24 additions & 0 deletions src/public/fs/mkdir/index.ts
@@ -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
);
};
}

0 comments on commit d0f6536

Please sign in to comment.