simplywatch
Version:
Watches files and upon change executes a command for each file INDIVIDUALLY with file-related params
148 lines (103 loc) • 4.25 kB
text/coffeescript
require('array-includes').shim()
require('object.values').shim()
Promise = require 'bluebird'
Path = require 'path'
EventfulPromise = require 'eventful-promise'; EventfulPromise.Promise = Promise
Glob = Promise.promisify require 'glob'
Console = require('console').Console
absPath = require 'abs'
extend = require 'smart-extend'
chalk = require 'chalk'
defaults = require './defaults'
Watcher = require './watcher'
File = require './file'
Queue = require './queue'
isModule = require './helpers/isModule'
debug =
init: require('debug')('simplywatch:init')
watch: require('debug')('simplywatch:watch')
instance: require('debug')('simplywatch:instance')
### istanbul ignore next ###
do ()->
try Promise.config cancellation:true
process.on 'warning', (e)-> console.warn(e.stack)
process.on 'unhandledRejection', (err)-> throw err
coerceToArray = (value)-> if Array.isArray(value) then value else [value]
coerceToNumber = (value)-> if typeof value is 'number' then not isNaN(value) else true
class WatchTask extends require('events')
constructor: (options)->
super()
= extend.allowNull.transform(
'globs': coerceToArray
'ignoreGlobs': coerceToArray
).filter(
'finalCommandDelay': coerceToNumber
'execDelay': coerceToNumber
'trim': coerceToNumber
)({fileCache:Object.create(null), scanCache:Object.create(null)}, defaults, options)
switch
when not .globs.length or .globs.some((glob)-> typeof glob isnt 'string')
throw new Error "No/Invalid globs were provided"
when not .command
throw new Error "Execution command not provided"
when not ['string','function'].some((type)=> typeof .command is type)
throw new Error "Invalid execution command provided: only a string or a callback may be provided"
when .finalCommand and not ['string','function'].some((type)=> typeof .finalCommand is type)
throw new Error "Invalid final execution command provided: only a string or a callback may be provided"
= new Console(.stdout, .stderr)
= new Queue(, @)
= new Watcher(.useFsEvents)
if .ignoreGlobs?.length
.options.ignored.push(glob) for glob in .ignoreGlobs
@.on 'childFile', (childFile)=> .add childFile.filePath, childFile.path
processGlob: (globToScan)->
debug.instance "scanning #{chalk.dim globToScan}"
Promise.delay()
.then ()-> Glob(globToScan, {nodir:true, dot:true})
.filter (filePath)=> not .isIgnored(filePath)
.map (filePath)=>
debug.init filePath
filePath = absPath(filePath)
unless filePath.includes('.git')
File.get({filePath, watchContext:globToScan}, , @) # Initiliaze a file constructor for this file
.scanProcedure
.then ()-> debug.instance "scan complete #{chalk.dim globToScan}"
processFile: (watchContext, eventType)-> (filePath)=>
filePath = absPath(filePath)
return if isModule(filePath, )
file = File.get {filePath, watchContext, canSkipRescan: not eventType}, , @
.add(file, eventType)
start: ()->
debug.instance 'start'
Promise.map .globs, (dirPath)=>
.on 'add',
.on 'change',
.add(dirPath, dirPath)
stop: ()->
.stop()
.stop()
createWatchTask = (options)->
debug.instance 'creating watch task'
task = new WatchTask(options)
EventfulPromise.resolve(task.start())
.then ()->
task.watcher.on 'ready', ()=>
debug.watch "WATCHER Ready"
task.watcher.on 'add', (file)=>
debug.watch "ADD #{chalk.dim file}"
task.watcher.on 'change', (file)=>
debug.watch "CHANGE #{chalk.dim file}"
task.watcher.on 'unlink', (file)=>
debug.watch "DELETE #{chalk.dim file}"
task.watcher.on 'error', (file)=>
debug.watch "ERROR #{chalk.dim file}"
.then ()-> task.watcher.ready
.return(task)
module.exports = createWatchTask
module.exports.WatchTask = WatchTask