Skip to content

Commit

Permalink
more complex filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
Yanis Benson committed Jun 28, 2019
1 parent d2b53cb commit 1955af9
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 20 deletions.
34 changes: 31 additions & 3 deletions index.d.ts
Expand Up @@ -2,6 +2,33 @@ import {GlobbyOptions} from 'globby';
import {Options as CpFileOptions} from 'cp-file';

declare namespace cpy {
interface SourceFile {
/**
Path to file.
*/
readonly path: string,

/**
Resolved path to file.
*/
readonly resolvedPath: string,

/**
File name.
*/
readonly name: string,

/**
File name without extension.
*/
readonly nameWithoutExtension: string,

/**
File extension.
*/
readonly extension: string,
}

interface Options extends Readonly<GlobbyOptions>, CpFileOptions {
/**
Working directory to find source files.
Expand Down Expand Up @@ -34,20 +61,21 @@ declare namespace cpy {
readonly rename?: string | ((basename: string) => string);

/**
Function to filter copied files. Return true to include, false to exclude. Can also return a Promise that resolves to true or false.
Function to filter files to copy. Should accept source file object as argument.
Return true to include, false to exclude. Can also return a Promise that resolves to true or false.
@example
```
import cpy = require('cpy');
(async () => {
await cpy('foo', 'destination', {
filter: name => !name.includes('NOCOPY')
filter: file => file.extension !== '.nocopy'
});
})();
```
*/
readonly filter?: (basename: string) => (boolean | Promise<boolean>);
readonly filter?: (file: SourceFile) => (boolean | Promise<boolean>);
}

interface ProgressData {
Expand Down
37 changes: 28 additions & 9 deletions index.js
Expand Up @@ -7,6 +7,25 @@ const cpFile = require('cp-file');
const pFilter = require('p-filter');
const CpyError = require('./cpy-error');

class SourceFile {
constructor(path, resolvedPath) {
this.path = path;
this.resolvedPath = resolvedPath;
}

get name() {
return path.basename(this.path);
}

get nameWithoutExtension() {
return path.basename(this.path, this.extension);
}

get extension() {
return path.extname(this.path);
}
}

const preprocessSourcePath = (source, options) => options.cwd ? path.resolve(options.cwd, source) : source;

const preprocessDestinationPath = (source, destination, options) => {
Expand Down Expand Up @@ -51,12 +70,14 @@ module.exports = (source, destination, options = {}) => {
throw new CpyError(`Cannot glob \`${source}\`: ${error.message}`, error);
}

let sources = files.map(sourcePath => new SourceFile(sourcePath, preprocessSourcePath(sourcePath, options)));

if (options.filter !== undefined) {
const filteredFiles = await pFilter(files, options.filter, {concurrency: 1024});
files = filteredFiles;
const filteredSources = await pFilter(sources, options.filter, {concurrency: 1024});
sources = filteredSources;
}

if (files.length === 0) {
if (sources.length === 0) {
progressEmitter.emit('progress', {
totalFiles: 0,
percent: 1,
Expand Down Expand Up @@ -90,14 +111,12 @@ module.exports = (source, destination, options = {}) => {
}
};

return Promise.all(files.map(async sourcePath => {
const from = preprocessSourcePath(sourcePath, options);
const to = preprocessDestinationPath(sourcePath, destination, options);

return Promise.all(sources.map(async source => {
const to = preprocessDestinationPath(source.path, destination, options);
try {
await cpFile(from, to, options).on('progress', fileProgressHandler);
await cpFile(source.resolvedPath, to, options).on('progress', fileProgressHandler);
} catch (error) {
throw new CpyError(`Cannot copy from \`${from}\` to \`${to}\`: ${error.message}`, error);
throw new CpyError(`Cannot copy from \`${source.path}\` to \`${to}\`: ${error.message}`, error);
}

return to;
Expand Down
4 changes: 2 additions & 2 deletions index.test-d.ts
Expand Up @@ -24,10 +24,10 @@ expectType<Promise<string[]> & ProgressEmitter>(
cpy('foo.js', 'destination', {overwrite: false})
);
expectType<Promise<string[]> & ProgressEmitter>(
cpy('foo.js', 'destination', {filter: () => true})
cpy('foo.js', 'destination', {filter: (file: cpy.SourceFile) => true})
);
expectType<Promise<string[]> & ProgressEmitter>(
cpy('foo.js', 'destination', {filter: async () => true})
cpy('foo.js', 'destination', {filter: async (file: cpy.SourceFile) => true})
);

expectType<Promise<string[]>>(
Expand Down
40 changes: 36 additions & 4 deletions readme.md
Expand Up @@ -94,22 +94,54 @@ const cpy = require('cpy');
})();
```

#### filter
##### filter

Type: `Function`

Function to filter copied files. Return true to include, false to exclude. Can also return a Promise that resolves to true or false.
Function to filter files to copy. Should accept source file object as argument.
Return true to include, false to exclude. Can also return a Promise that resolves to true or false.

```js
const cpy = require('cpy');

(async () => {
await cpy('foo.js', 'destination', {
filter: name => !name.includes('NOCOPY')
await cpy('foo', 'destination', {
filter: file => file.extension !== '.nocopy'
});
})();
```

##### Source file object

###### path

Type: `string`

Path to file.

###### resolvedPath

Type: `string`

Resolved path to file.

###### name

Type: `string`

File name.

###### nameWithoutExtension

Type: `string`

File name without extension.

###### extension

Type: `string`

File extension.

## Progress reporting

Expand Down
22 changes: 20 additions & 2 deletions test.js
Expand Up @@ -50,14 +50,32 @@ test('copy array of files', async t => {
});

test('copy array of files with filter', async t => {
await cpy(['license', 'package.json'], t.context.tmp, {filter: name => name !== 'license'});
await cpy(['license', 'package.json'], t.context.tmp, {
filter: file => {
t.is(typeof file.path, 'string');
t.is(typeof file.resolvedPath, 'string');
t.is(typeof file.name, 'string');
t.is(typeof file.nameWithoutExtension, 'string');
t.is(typeof file.extension, 'string');
return file.path !== 'license';
}
});

t.false(fs.existsSync(path.join(t.context.tmp, 'license')));
t.is(read('package.json'), read(t.context.tmp, 'package.json'));
});

test('copy array of files with async filter', async t => {
await cpy(['license', 'package.json'], t.context.tmp, {filter: async name => name !== 'license'});
await cpy(['license', 'package.json'], t.context.tmp, {
filter: async file => {
t.is(typeof file.path, 'string');
t.is(typeof file.resolvedPath, 'string');
t.is(typeof file.name, 'string');
t.is(typeof file.nameWithoutExtension, 'string');
t.is(typeof file.extension, 'string');
return file.path !== 'license';
}
});

t.false(fs.existsSync(path.join(t.context.tmp, 'license')));
t.is(read('package.json'), read(t.context.tmp, 'package.json'));
Expand Down

0 comments on commit 1955af9

Please sign in to comment.