diff --git a/index.d.ts b/index.d.ts index 2edc30e..04cc66f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -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, CpFileOptions { /** Working directory to find source files. @@ -34,7 +61,8 @@ 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 ``` @@ -42,12 +70,12 @@ declare namespace cpy { (async () => { await cpy('foo', 'destination', { - filter: name => !name.includes('NOCOPY') + filter: file => file.extension !== '.nocopy' }); })(); ``` */ - readonly filter?: (basename: string) => (boolean | Promise); + readonly filter?: (file: SourceFile) => (boolean | Promise); } interface ProgressData { diff --git a/index.js b/index.js index daa4858..ce2e5ac 100644 --- a/index.js +++ b/index.js @@ -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) => { @@ -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, @@ -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; diff --git a/index.test-d.ts b/index.test-d.ts index cb90ee7..9fa57cf 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -24,10 +24,10 @@ expectType & ProgressEmitter>( cpy('foo.js', 'destination', {overwrite: false}) ); expectType & ProgressEmitter>( - cpy('foo.js', 'destination', {filter: () => true}) + cpy('foo.js', 'destination', {filter: (file: cpy.SourceFile) => true}) ); expectType & ProgressEmitter>( - cpy('foo.js', 'destination', {filter: async () => true}) + cpy('foo.js', 'destination', {filter: async (file: cpy.SourceFile) => true}) ); expectType>( diff --git a/readme.md b/readme.md index c3a1d73..d8ea511 100644 --- a/readme.md +++ b/readme.md @@ -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 diff --git a/test.js b/test.js index f644011..4711827 100644 --- a/test.js +++ b/test.js @@ -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'));