Skip to content

Commit

Permalink
Merge pull request #32 from bcomnes/glob-gitignore
Browse files Browse the repository at this point in the history
Add gitignore style ignore support
  • Loading branch information
bcomnes authored Sep 15, 2021
2 parents 5f741c4 + be9680c commit 4c28108
Show file tree
Hide file tree
Showing 14 changed files with 184 additions and 72 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Options:
-c, --command <command> A command text to transform each file.
-C, --clean Clean files that matches <source> like pattern in
<dest> directory before the first copying.
-i --ignore A comma separated list of gitignore style ignore
patterns.
-L, --dereference Follow symbolic links when copying from them.
-h, --help Print usage information.
--include-empty-dirs The flag to copy empty directories which is
Expand Down Expand Up @@ -110,6 +112,7 @@ cpx.copy(source, dest, callback)
- **options.preserve** `{boolean}` -- The flag to copy uid, gid, atime, and mtime of files. Default: `false`.
- **options.transform** `{((filepath: string) => stream.Transform)[]}` -- Functions that creates a `stream.Transform` object to transform each copying file.
- **options.update** `{boolean}` -- The flag to not overwrite files on destination if the source file is older. Default: `false`.
- **options.ignore** `{string|Array<string>}` -- A gitignore style string or array of strings that make ignoring directory patterns easier. Default: []
- **callback** `{(err: Error|null) => void}` -- A function that is called at done.

Copy files that matches with `source` glob to `dest` directory.
Expand Down
2 changes: 2 additions & 0 deletions bin/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Options:
-c, --command <command> A command text to transform each file.
-C, --clean Clean files that matches <source> like pattern in
<dest> directory before the first copying.
-i --ignore A comma separated list of gitignore style ignore
patterns.
-L, --dereference Follow symbolic links when copying from them.
-h, --help Print usage information.
--include-empty-dirs The flag to copy empty directories which is
Expand Down
2 changes: 2 additions & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const args = subarg(process.argv.slice(2), {
C: "clean",
h: "help",
includeEmptyDirs: "include-empty-dirs",
i: "ignore",
L: "dereference",
p: "preserve",
t: "transform",
Expand All @@ -44,6 +45,7 @@ const args = subarg(process.argv.slice(2), {
"version",
"watch",
],
string: ["ignore"],
default: { initial: true },
unknown(arg) {
if (arg[0] === "-") {
Expand Down
1 change: 1 addition & 0 deletions bin/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ module.exports = function main(source, outDir, args) {
initialCopy: args.initial,
preserve: args.preserve,
update: args.update,
ignore: args.ignore && args.ignore.split(","),
})

if (args.clean) {
Expand Down
1 change: 1 addition & 0 deletions lib/copy-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const removeFileSync = require("./utils/remove-file-sync")
* @param {boolean} [options.initialCopy=true] The flag to copy files at the first time.
* @param {boolean} [options.preserve=false] The flag to copy file attributes such as timestamps, users, and groups.
* @param {boolean} [options.update=false] The flag to not overwrite newer files.
* @param {string|Array.<string>} [options.ignore] - gitignore string or array of gitignore strings
* @returns {void}
*/
module.exports = function copySync(source, outputDir, options) {
Expand Down
1 change: 1 addition & 0 deletions lib/copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const removeFile = require("./utils/remove-file")
* @param {boolean} [options.preserve=false] The flag to copy file attributes such as timestamps, users, and groups.
* @param {(function(string):void)[]} [options.transform] The array of the factories of transform streams.
* @param {boolean} [options.update=false] The flag to not overwrite newer files.
* @param {string|Array.<string>} [options.ignore] - gitignore string or array of gitignore strings
* @param {function(Error):void} [callback] The callback function which will go fulfilled after done.
* @returns {Promise<void>} The promise which will go fulfilled after done.
*/
Expand Down
4 changes: 3 additions & 1 deletion lib/utils/apply-action-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// Requirements
//------------------------------------------------------------------------------

const glob = require("glob")
const glob = require("glob-gitignore")

//------------------------------------------------------------------------------
// Exports
Expand All @@ -22,6 +22,7 @@ const glob = require("glob")
* @param {object} options - The option object.
* @param {boolean} [options.includeEmptyDirs=false] - The flag to include empty directories to copy.
* @param {boolean} [options.dereference=false] - The flag to dereference symbolic links.
* @param {string|Array.<String>} [options.ignore] - gitignore string or array of gitignore strings
* @param {function(string):void} action - The action function to apply.
* @returns {Promise<void>} The promise which will go fulfilled after done.
* @private
Expand All @@ -31,6 +32,7 @@ module.exports = function applyActionSync(pattern, options, action) {
nodir: !options.includeEmptyDirs,
silent: true,
follow: Boolean(options.dereference),
ignore: options.ignore,
}
for (const sourcePath of glob.sync(pattern, globOptions)) {
action(sourcePath)
Expand Down
78 changes: 13 additions & 65 deletions lib/utils/apply-action.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
// Requirements
//------------------------------------------------------------------------------

const glob = require("glob")
const glob = require("glob-gitignore")
const pMap = require("p-map")

//------------------------------------------------------------------------------
// Exports
Expand All @@ -22,73 +23,20 @@ const glob = require("glob")
* @param {object} options - The option object.
* @param {boolean} [options.includeEmptyDirs=false] - The flag to include empty directories to copy.
* @param {boolean} [options.dereference=false] - The flag to dereference symbolic links.
* @param {string|Array.<string>} [options.ignore] - gitignore string or array of gitignore strings
* @param {function(string):void} action - The action function to apply.
* @returns {Promise<void>} The promise which will go fulfilled after done.
* @private
*/
module.exports = function applyAction(pattern, options, action) {
return new Promise((resolve, reject) => {
let count = 0
let done = false
let lastError = null
module.exports = async function applyAction(pattern, options, action) {
const globOptions = {
nodir: !options.includeEmptyDirs,
silent: true,
follow: Boolean(options.dereference),
nosort: true,
ignore: options.ignore,
}
const sourcePaths = await glob.glob(pattern, globOptions)

/**
* Calls the callback function if done.
* @returns {void}
*/
function next() {
if (done && count === 0) {
if (lastError == null) {
resolve()
} else {
reject(lastError)
}
}
}

const globOptions = {
nodir: !options.includeEmptyDirs,
silent: true,
follow: Boolean(options.dereference),
nosort: true,
}
try {
new glob.Glob(pattern, globOptions)
.on("match", sourcePath => {
if (lastError != null) {
return
}

count += 1
try {
action(sourcePath).then(
() => {
count -= 1
next()
},
error => {
count -= 1
lastError = lastError || error
next()
}
)
} catch (error) {
count -= 1
lastError = lastError || error
next()
}
})
.on("end", () => {
done = true
next()
})
.on("error", error => {
done = true
lastError = lastError || error
next()
})
} catch (error) {
reject(error)
}
})
return pMap(sourcePaths, action, { concurrency: 5 })
}
4 changes: 3 additions & 1 deletion lib/utils/normalize-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ function getBasePath(source) {
* @param {boolean} [options.preserve=false] The flag to copy file attributes such as timestamps, users, and groups.
* @param {(function(string):stream.Transform)[]} [options.transform=null] The array of transform function's factories.
* @param {boolean} [options.update=false] The flag to not overwrite newer files.
* @returns {{baseDir:string,clean:boolean,dereference:boolean,includeEmptyDirs:boolean,initialCopy:boolean,outputDir:string,preserve:boolean,source:string,transform:any[],toDestination:any,update:boolean}} The normalized options.
* @param {string|Array.<string>} [options.ignore] - gitignore string or array of gitignore strings
* @returns {{baseDir:string,clean:boolean,dereference:boolean,includeEmptyDirs:boolean,initialCopy:boolean,outputDir:string,preserve:boolean,source:string,transform:any[],toDestination:any,update:boolean,ignore:string|Array.<string>}} The normalized options.
* @private
*/
module.exports = function normalizeOptions(source, outputDir, options) {
Expand All @@ -69,5 +70,6 @@ module.exports = function normalizeOptions(source, outputDir, options) {
toDestination,
transform: [].concat(options && options.transform).filter(Boolean),
update: Boolean(options && options.update),
ignore: options && options.ignore,
}
}
28 changes: 27 additions & 1 deletion lib/utils/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const debounce = require("debounce")
const debug = require("debug")("cpx")
const fs = require("fs-extra")
const Minimatch = require("minimatch").Minimatch
const ignore = require("ignore")
const copyFile = require("./copy-file")
const normalizePath = require("./normalize-path")
const removeFile = require("./remove-file")
Expand All @@ -24,7 +25,7 @@ const removeFile = require("./remove-file")
// Helpers
//------------------------------------------------------------------------------

const walkDirectories = co.wrap(function*(dirRoot, dereference, callback) {
const walkDirectories = co.wrap(function*(dirRoot, dereference, ig, callback) {
const stack = []

// Check whether the root is a directory.
Expand Down Expand Up @@ -52,6 +53,14 @@ const walkDirectories = co.wrap(function*(dirRoot, dereference, callback) {
const childPath = normalizePath(path.join(entry.path, child))
const childStat = yield fs.stat(childPath)

if (
ig.ignores(
childStat.isDirectory() ? `${childPath}/` : childPath
)
) {
continue
}

entry.files.set(childPath, childStat)

if (childStat.isDirectory()) {
Expand Down Expand Up @@ -90,6 +99,7 @@ module.exports = class Watcher extends EventEmitter {
* @param {string} options.source The glob pattern of source files.
* @param {(function(string):stream.Transform)[]} options.transform The array of transform function's factories.
* @param {boolean} options.update The flag to not overwrite newer files.
* @param {string|Array.<String>} [options.ignore] - gitignore string or array of gitignore strings.
*/
constructor(options) {
super()
Expand All @@ -100,6 +110,7 @@ module.exports = class Watcher extends EventEmitter {
this.includeEmptyDirs = options.includeEmptyDirs
this.initialCopy = options.initialCopy
this.matcher = new Minimatch(options.source)
this.ignore = ignore().add(options.ignore)
this.outputDir = options.outputDir
this.preserve = options.preserve
this.source = options.source
Expand Down Expand Up @@ -168,6 +179,7 @@ module.exports = class Watcher extends EventEmitter {
return walkDirectories(
dirRoot,
this.dereference,
this.ignore,
co.wrap(
function*(dirPath, files) {
if (this.trigger == null || this.watchers.has(dirPath)) {
Expand Down Expand Up @@ -354,6 +366,10 @@ module.exports = class Watcher extends EventEmitter {
return
}

if (this.ignore.ignores(normalizedPath)) {
return
}

if (this.ready) {
this.enqueueAdd(normalizedPath)
} else if (this.initialCopy) {
Expand All @@ -374,6 +390,11 @@ module.exports = class Watcher extends EventEmitter {
onRemoved(sourcePath) {
debug("Watcher#onRemoved", sourcePath)
const normalizedPath = normalizePath(sourcePath)

if (this.ignore.ignores(normalizedPath)) {
return
}

if (this.matcher.match(normalizedPath)) {
this.enqueueRemove(normalizedPath)
}
Expand All @@ -388,6 +409,11 @@ module.exports = class Watcher extends EventEmitter {
onChanged(sourcePath) {
debug("Watcher#onChanged", sourcePath)
const normalizedPath = normalizePath(sourcePath)

if (this.ignore.ignores(normalizedPath)) {
return
}

if (this.matcher.match(normalizedPath)) {
this.enqueueChange(normalizedPath)
}
Expand Down
1 change: 1 addition & 0 deletions lib/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const Watcher = require("./utils/watcher")
* @param {boolean} [options.preserve=false] The flag to copy file attributes such as timestamps, users, and groups.
* @param {(function(string):void)[]} [options.transform] The array of the factories of transform streams.
* @param {boolean} [options.update=false] The flag to not overwrite newer files.
* @param {string|Array.<string>} [options.ignore] - gitignore string or array of gitignore strings
* @returns {Watcher} The watcher object which observes the files.
*/
module.exports = function watch(source, outputDir, options) {
Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "3.0.2",
"description": "Copy file globs, watching for changes.",
"engines": {
"node": ">=6.5"
"node": ">=14"
},
"main": "lib/index.js",
"bin": {
Expand All @@ -30,22 +30,24 @@
"debug": "^4.1.1",
"duplexer": "^0.1.1",
"fs-extra": "^10.0.0",
"glob": "^7.1.4",
"glob-gitignore": "^1.0.14",
"glob2base": "0.0.12",
"ignore": "^5.1.8",
"minimatch": "^3.0.4",
"p-map": "^4.0.0",
"resolve": "^1.12.0",
"safe-buffer": "^5.2.0",
"shell-quote": "^1.7.1",
"subarg": "^1.0.0"
},
"devDependencies": {
"auto-changelog": "^2.2.0",
"gh-release": "^6.0.0",
"@babel/core": "^7.5.5",
"@babel/register": "^7.5.5",
"@mysticatea/eslint-plugin": "^13.0.0",
"auto-changelog": "^2.2.0",
"babel-preset-power-assert": "^3.0.0",
"eslint": "^7.9.0",
"gh-release": "^6.0.0",
"mocha": "^9.0.3",
"nyc": "15.1.0",
"opener": "^1.5.1",
Expand Down
Loading

0 comments on commit 4c28108

Please sign in to comment.