From 0934ab14850acfa278a5fcfdb95b91cc10b177f9 Mon Sep 17 00:00:00 2001 From: Kasper Mikiewicz Date: Sun, 27 Feb 2022 10:25:50 +0100 Subject: [PATCH] Fix recursive bugs (#92) Co-authored-by: Sindre Sorhus --- glob-pattern.js | 2 ++ index.js | 40 ++++++++++++++++++++++++++++++++++++---- package.json | 1 + 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/glob-pattern.js b/glob-pattern.js index ef6c178..6e0d53b 100644 --- a/glob-pattern.js +++ b/glob-pattern.js @@ -14,6 +14,7 @@ export default class GlobPattern { this.originalPath = pattern; this.destination = destination; this.options = options; + this.isDirectory = false; if ( !isDynamicPattern(pattern) @@ -21,6 +22,7 @@ export default class GlobPattern { && fs.lstatSync(pattern).isDirectory() ) { this.path = [pattern, '**'].join('/'); + this.isDirectory = true; } } diff --git a/index.js b/index.js index abb6349..57c0314 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ import arrify from 'arrify'; import cpFile from 'cp-file'; import pFilter from 'p-filter'; import {isDynamicPattern} from 'globby'; +import micromatch from 'micromatch'; import CpyError from './cpy-error.js'; import GlobPattern from './glob-pattern.js'; @@ -55,6 +56,24 @@ class Entry { } } +/** +Expand patterns like `'node_modules/{globby,micromatch}'` into `['node_modules/globby', 'node_modules/micromatch']`. + +@param {string[]} patterns +@returns {string[]} +*/ +const expandPatternsWithBraceExpansion = patterns => { + let collection = []; + for (const pattern of patterns) { + collection = [...collection, ...micromatch.braces(pattern, { + expand: true, + nodupes: true, + })]; + } + + return collection; +}; + /** @param {object} props @param {Entry} props.entry @@ -82,6 +101,15 @@ const preprocessDestinationPath = ({entry, destination, options}) => { return path.join(destination, entry.name); } + // TODO: This check will not work correctly if `options.cwd` and `entry.path` are on different partitions on Windows, see: https://github.com/sindresorhus/import-local/pull/12 + if (entry.pattern.isDirectory && path.relative(options.cwd, entry.path).startsWith('..')) { + return path.join(options.cwd, destination, path.basename(entry.pattern.originalPath), path.relative(entry.pattern.originalPath, entry.path)); + } + + if (!entry.pattern.isDirectory && entry.path === entry.relativePath) { + return path.join(options.cwd, destination, path.basename(entry.pattern.originalPath), path.relative(entry.pattern.originalPath, entry.path)); + } + return path.join(options.cwd, destination, path.relative(options.cwd, entry.path)); }; @@ -136,16 +164,20 @@ export default function cpy( let entries = []; let completedFiles = 0; let completedSize = 0; + /** @type {GlobPattern[]} */ - let patterns = arrify(source).map(string => string.replace(/\\/g, '/')); + let patterns = expandPatternsWithBraceExpansion(arrify(source)) + .map(string => string.replace(/\\/g, '/')); + const sources = patterns.filter(item => !item.startsWith('!')); + const ignore = patterns.filter(item => item.startsWith('!')); - if (patterns.length === 0 || !destination) { + if (sources.length === 0 || !destination) { throw new CpyError('`source` and `destination` required'); } - patterns = patterns.map(pattern => new GlobPattern(pattern, destination, options)); + patterns = patterns.map(pattern => new GlobPattern(pattern, destination, {...options, ignore})); for (const pattern of patterns) { /** @@ -162,7 +194,7 @@ export default function cpy( ); } - if (matches.length === 0 && !isDynamicPattern(pattern.originalPath)) { + if (matches.length === 0 && !isDynamicPattern(pattern.originalPath) && !isDynamicPattern(ignore)) { throw new CpyError( `Cannot copy \`${pattern.originalPath}\`: the file doesn't exist`, ); diff --git a/package.json b/package.json index b0f003e..39c72e8 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "cp-file": "^9.1.0", "globby": "^12.0.2", "junk": "^4.0.0", + "micromatch": "^4.0.4", "nested-error-stacks": "^2.1.0", "p-filter": "^3.0.0", "p-map": "^5.3.0"