Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add gitignore style ignore support #32

Merged
merged 2 commits into from
Sep 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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