Skip to content

Bugfix/empty folders windows #46

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

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d36d155
added should copy empty sub dir test
AndyOGo Apr 11, 2017
abdb43a
added empty sub dir deletion test
AndyOGo Apr 11, 2017
3749bdb
ooops, fixed wrong path
AndyOGo Apr 11, 2017
7dccae7
added log of initial mirror
AndyOGo Apr 11, 2017
f8ec639
moar log
AndyOGo Apr 11, 2017
05d0d4a
fixed missing path normalization
AndyOGo Apr 11, 2017
17c3c3d
fixed double slashh
AndyOGo Apr 11, 2017
9b894ed
updated fs-extra
AndyOGo Apr 11, 2017
ab4a455
normalized target path
AndyOGo Apr 11, 2017
f7be43d
added conditional raw logging
AndyOGo Apr 11, 2017
803fa96
added dir diff set logging
AndyOGo Apr 11, 2017
056145d
ooops typo
AndyOGo Apr 11, 2017
a647028
start adding debug option
AndyOGo Apr 11, 2017
14f13c8
utilize debug
AndyOGo Apr 11, 2017
a03d5d2
updated sync empty dir del spec
AndyOGo Apr 11, 2017
ad2a6ec
beautified globed files los
AndyOGo Apr 11, 2017
0d9f4e1
test wheter bar exists
AndyOGo Apr 11, 2017
323bccf
improve raw log
AndyOGo Apr 11, 2017
4dcb624
added all debug
AndyOGo Apr 11, 2017
46b8730
utilize inspect
AndyOGo Apr 11, 2017
49220ea
just debug broken test
AndyOGo Apr 11, 2017
79070bb
added cwd
AndyOGo Apr 12, 2017
80c9dec
enabled usePolling and useFsEvents
AndyOGo Apr 12, 2017
9be0c79
added empty file
AndyOGo Apr 12, 2017
c9ebee3
added empty file test
AndyOGo Apr 12, 2017
4c4f124
disabled awaitWriteFinish
AndyOGo Apr 12, 2017
131dd64
undo removal of awaitWriteFinish
AndyOGo Apr 12, 2017
2ff0a7c
npm i graceful-fs
AndyOGo Jul 6, 2017
c2ffb12
utilize graceful fs
AndyOGo Jul 6, 2017
1246f80
disable eslint import rule
AndyOGo Jul 6, 2017
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: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@
"bluebird": "^3.4.7",
"chalk": "^1.1.3",
"chokidar": "^1.6.1",
"fs-extra": "^1.0.0",
"fs-extra": "^2.1.2",
"glob-all": "^3.1.0",
"graceful-fs": "^4.1.11",
"yargs": "^6.3.0"
},
"devDependencies": {
Expand Down
5 changes: 5 additions & 0 deletions src/bin/sync-glob.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ const argv = yargs.usage('Usage: $0 <sources> <target>')
.alias('v', 'verbose')
.default('verbose', false)
.describe('verbose', 'Moar output')
.boolean('debug')
.alias('b', 'debug')
.default('debug', false)
.describe('debug', 'Log essential information for debugging')
.version()
.help('help')
.showHelpOnFail(false, 'Specify --help for available options')
Expand Down Expand Up @@ -67,6 +71,7 @@ const close = syncGlob(sources, target, {
watch: argv.watch,
delete: argv.delete,
depth: argv.depth || Infinity,
debug: argv.debug,
transform: argv.transform,
}, (event, data) => {
const priority = notifyPriority[event] || 'low'
Expand Down
42 changes: 37 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/* globals process */

import fs from 'fs'
import gracefulFs from 'graceful-fs'

gracefulFs.gracefulify(fs)
/* eslint-disable import/first */
import path from 'path'
import util from 'util'
import globAll from 'glob-all'
import chokidar from 'chokidar'
import Promise, { promisify } from 'bluebird'
Expand All @@ -18,6 +23,7 @@ const defaults = {
watch: false,
delete: true,
depth: Infinity,
debug: false,
}

/**
Expand All @@ -29,6 +35,7 @@ const defaults = {
* @param {bool} [options.watch=false] - Enable or disable watch mode.
* @param {bool} [options.delete=true] - Whether to delete the `target`'s content initially.
* @param {bool} [options.depth=Infinity] - Chokidars `depth` (If set, limits how many levels of subdirectories will be traversed).
* @param {bool} [options.debug=false] - Log essential information for debugging.
* @param {string} [options.transform=false] - A module path resolved by node's `require`.
* @param {NotifyCallback} [notify] - An optional notification callback.
* @returns {CloseFunc} - Returns a close function which cancels active promises and watch mode.
Expand All @@ -41,6 +48,9 @@ const syncGlob = (sources, target, options = {}, notify = () => {}) => {
}
// eslint-disable-next-line no-param-reassign
sources = sources.map(trimQuotes)
const originalTarget = target
// eslint-disable-next-line no-param-reassign
target = path.normalize(target)

if (typeof options === 'function') {
// eslint-disable-next-line no-param-reassign
Expand All @@ -58,7 +68,7 @@ const syncGlob = (sources, target, options = {}, notify = () => {}) => {
const notifyError = (err) => { notify('error', err) }
const bases = sourcesBases(sources)
const resolveTargetFromBases = resolveTarget(bases)
const { depth, watch } = options
const { depth, watch, debug } = options
let { transform } = options

if (typeof depth !== 'number' || isNaN(depth)) {
Expand Down Expand Up @@ -86,10 +96,19 @@ const syncGlob = (sources, target, options = {}, notify = () => {}) => {
}

// Initial mirror
const initSources = sources.map(source => (isGlob(source) === -1
&& fs.statSync(path.normalize(source)).isDirectory() ? `${source}${source.slice(-1) === '/' ? '' : '/'}**` : source))
const mirrorInit = [
promisify(globAll)(sources.map(source => (isGlob(source) === -1
&& fs.statSync(source).isDirectory() ? `${source}/**` : source)))
.then(files => files.map(file => path.normalize(file))),
promisify(globAll)(initSources)
.then(files => files.map(file => path.normalize(file)))
.then((files) => {
if (debug) {
console.log(`sources: ${sources} -> ${initSources}`)
console.log(`target: ${originalTarget} -> ${target}`)
console.log(`globed files: \n\t${files.join('\n\t')}`)
}
return files
}),
]

if (options.delete) {
Expand Down Expand Up @@ -160,17 +179,24 @@ const syncGlob = (sources, target, options = {}, notify = () => {}) => {
// Watcher to keep in sync from that
if (watch) {
watcher = chokidar.watch(sources, {
cwd: process.cwd(),
persistent: true,
depth,
ignoreInitial: true,
awaitWriteFinish: true,
usePolling: true,
useFsEvents: true,
})

watcher.on('ready', notify.bind(undefined, 'watch', sources))
.on('all', (event, source) => {
.on('all', (event, source, stats) => {
const resolvedTarget = resolveTargetFromBases(source, target)
let promise

if (debug) {
console.log(`ALL: ${event} -> ${source} ${stats ? `\t\n${util.inspect(stats)}` : ''}`)
}

switch (event) {
case 'add':
case 'change':
Expand Down Expand Up @@ -218,6 +244,12 @@ const syncGlob = (sources, target, options = {}, notify = () => {}) => {
})
.on('error', notifyError)

if (debug) {
watcher.on('raw', (event, rpath, details) => {
console.log(`RAW: ${event} -> ${rpath} \t\n${util.inspect(details)}`)
})
}

process.on('SIGINT', close)
process.on('SIGQUIT', close)
process.on('SIGTERM', close)
Expand Down
22 changes: 21 additions & 1 deletion test/copy.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import syncGlob from '../src/index'
import { beforeEachSpec, afterAllSpecs, awaitCount, awaitMatch, compare, compareDir, noop } from './helpers'
import { beforeEachSpec, afterAllSpecs, fs, awaitCount, awaitMatch, compare, compareDir, noop } from './helpers'

describe('node-sync-glob copy', () => {
beforeEach(beforeEachSpec)
Expand Down Expand Up @@ -111,4 +111,24 @@ describe('node-sync-glob copy', () => {
'mirror', compareDir(done, 'tmp/mock', 'tmp/copy')
))
})

it('should copy empty sub directories', (done) => {
fs.ensureDirSync('tmp/mock/bar/empty')

const close = syncGlob('tmp/mock/**/*', 'tmp/copy', awaitMatch(
'error', (err) => {
fail(err)
close()
done()
},
'mirror', () => {
expect(fs.existsSync('tmp/copy/bar/empty')).toBe(true)

compareDir(null, 'tmp/mock', 'tmp/copy')

close()
done()
}
))
})
})
27 changes: 27 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const fs = {
appendFileSync: (source, ...args) => fsExtra.appendFileSync(path.normalize(source), ...args),
existsSync: source => fsExtra.existsSync(path.normalize(source)),
readFileSync: source => fsExtra.readFileSync(path.normalize(source)),
ensureDirSync: source => fsExtra.ensureDirSync(path.normalize(source)),
}

export const beforeEachSpec = () => {
Expand Down Expand Up @@ -106,6 +107,28 @@ export const awaitMatch = (...args) => {
}
}

const logDirDiffSet = (source, target, res) => {
if (!res.same) {
const logs = res.diffSet.map((entry) => {
const state = {
equal: '==',
left: '->',
right: '<-',
distinct: '<>',
}[entry.state]
const name1 = entry.name1 ? entry.name1 : ''
const name2 = entry.name2 ? entry.name2 : ''
const path1 = entry.path1 ? entry.path1 : ''
const path2 = entry.path2 ? entry.path2 : ''
const { type1, type2 } = entry

return `${path1}/${name1} ${type1} ${state} ${path2}/${name2} ${type2}`
})

console.log(`${source} -> ${target}\n\t${logs.join('\n\t')}`)
}
}

export const compare = (done, source, target, options) => (event, data) => {
if (event) {
if (Array.isArray(data) && data.length === 2
Expand All @@ -120,6 +143,8 @@ export const compare = (done, source, target, options) => (event, data) => {
compareContent: true,
})

logDirDiffSet(source, target, res)

expect(res.differences).toBe(0)
expect(res.differencesFiles).toBe(0)
expect(res.distinctFiles).toBe(0)
Expand All @@ -140,6 +165,8 @@ export const compareDir = (done, source, target, options = {}) => (event) => {
compareContent: true,
})

logDirDiffSet(source, target, res)

expect(res.differences).toBe(0)
expect(res.differencesFiles).toBe(0)
expect(res.distinctFiles).toBe(0)
Expand Down
Empty file added test/mock/emptyFile
Empty file.
52 changes: 51 additions & 1 deletion test/sync.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('node-sync-glob watch', () => {
})

it('should sync a directory', (done) => {
const close = syncGlob('tmp/mock/foo', 'tmp/sync/', { watch }, awaitMatch(
const close = syncGlob('tmp/mock/foo', 'tmp/sync', { watch }, awaitMatch(
'error', (err) => {
fail(err)
close()
Expand Down Expand Up @@ -93,4 +93,54 @@ describe('node-sync-glob watch', () => {
}
))
})

it('should sync empty sub directory deletion', (done) => {
try {
console.log(`EXISTS: tmp/mock/bar -> ${fs.existsSync('tmp/mock/bar')}`)
fs.ensureDirSync('tmp/mock/bar/empty')

const close = syncGlob('tmp/mock/**/*', 'tmp/sync', { watch, debug: true }, awaitMatch(
'error', (err) => {
fail(err)
close()
done()
},
['mirror', 'watch'], compareDir(() => {
fs.removeSync('tmp/mock/bar/empty')
}, 'tmp/mock', 'tmp/sync'),
'remove', () => {
expect(fs.existsSync('tmp/sync/foo/b.txt')).toBe(true)
expect(fs.existsSync('tmp/sync/bar/empty')).toBe(false)

close()
done()
}
))
} catch (err) {
console.log(err)

fail(err)
done()
}
})

it('should sync empty file deletion', (done) => {
const close = syncGlob('tmp/mock/**/*', 'tmp/sync', { watch, debug: true }, awaitMatch(
'error', (err) => {
fail(err)
close()
done()
},
['mirror', 'watch'], compareDir(() => {
fs.removeSync('tmp/mock/emptyFile')
}, 'tmp/mock', 'tmp/sync'),
'remove', () => {
expect(fs.existsSync('tmp/sync/foo/b.txt')).toBe(true)
expect(fs.existsSync('tmp/sync/emptyFile')).toBe(false)

close()
done()
}
))
})
})