flamingo-carotene-dev-server
Version:
Provide the functionality to add file watchers for developing projects with the Flamingo Carotene tooling
225 lines (180 loc) • 6.83 kB
JavaScript
const chokidar = require('chokidar')
/**
* This class is a wrapper for a watcher instance
*/
class FileWatcher {
/**
* @param socket - The socket instance
* @param core - Flamingo Carotene core
* @param watcherConfig - Config object for the file watcher
* watchId - a unique watchId
* watchPaths - Array of paths with globbing to be watched
* command - Flamingo Carotene command, that will be triggered
* callbackKey - key of config[KEY].callback function, that will be called
* watcherConfig - (optional) config object to be passed to the chokidar file watcher
* unwatchConfig - (optional) string or array of strings of file-, folder-, or glob-paths
* @param devServer
* @param beforeRunCallback
* @param afterRunCallback
*/
constructor (socket, core, watcherConfig, devServer, beforeRunCallback, afterRunCallback) {
// Socket to client
this.socket = socket
this.core = core;
// Flamingo Carotene dispatcher
this.dispatcher = this.core.getDispatcher()
// Flamingo Carotene cliTools
this.cliTools = this.core.getCliTools()
this.jobManager = this.core.getJobmanager();
// Flamingo Carotene config
this.config = this.core.getConfig()
this.watcherVerbose = this.cliTools.hasOption('--verboseWatch')
this.devServer = devServer
this.beforeRunCallback = beforeRunCallback;
this.afterRunCallback = afterRunCallback;
watcherConfig = watcherConfig || {}
this.watchId = watcherConfig.watchId
this.watchPaths = watcherConfig.path
if (this.watchPaths) {
if (typeof this.watchPaths === 'string') {
this.watchPaths = [this.watchPaths]
}
} else {
this.watchPaths = []
}
this.unwatchConfig = watcherConfig.unwatchConfig
if (this.unwatchConfig) {
if (typeof this.unwatchConfig === 'string') {
this.unwatchConfig = [this.unwatchConfig]
}
} else {
this.unwatchConfig = []
}
this.command = watcherConfig.command
this.callbackKey = watcherConfig.callbackKey
if (!this.callbackKey) {
this.cliTools.error('Filewatcher was started without callbackKey (jobmanager groupid)')
}
this.watcherConfig = Object.assign(
this.config.devServer.watcherConfig || {},
watcherConfig.watcherConfig || {}
)
this.socketCommand = watcherConfig.socketCommand || 'built'
// instance of chokidar
this.watcher = null
// flag, if this watcher is currently running a build
this.buildInProgress = false
// flag, if the watcher needs to rerun after the build is finished
this.rerunAfterBuild = false
// buffer, where a original callback is stored, as long as it is overwritten
this.originalBuildCallback = null
// if a build is triggered, the file-path, which has trigger the change
this.currentChangedPath = ''
this.initialize()
}
/**
* Returns information if the watcher has an active build process
* @returns {boolean}
*/
isBuildInProgress () {
return this.buildInProgress
}
removeBasePathFromPath (path) {
const basePath = this.config.paths.src
if (path.substr(0, basePath.length) === basePath) {
path = path.substr(basePath.length)
}
return path
}
/**
* Setup watcher
*/
initialize () {
// filter basePath in display
const showWatchPaths = []
for (const watchPath of this.watchPaths) {
showWatchPaths.push(this.removeBasePathFromPath(watchPath))
}
const showUnwatchPaths = []
for (const unwatchPath of this.unwatchConfig) {
showUnwatchPaths.push(this.removeBasePathFromPath(unwatchPath))
}
// output state in CLI
this.cliTools.info(`Watcher-${this.watchId} listens to ${showWatchPaths.join(', ')}`, !this.watcherVerbose)
if (showUnwatchPaths.length > 0) {
this.cliTools.info(` - except: ${showUnwatchPaths.join(', ')}`, !this.watcherVerbose)
}
// chokidar dont like windows \ in paths
// replacing them with / works
// this cant be done via path.posix, because of globbing :D
if (process.platform === 'win32') {
const windowsPaths = []
for (const watchPath of this.watchPaths) {
windowsPaths.push(watchPath.split('\\').join('/'))
}
this.watchPaths = windowsPaths
const windowsUnwatchPaths = []
for (const unwatchPath of this.unwatchConfig) {
windowsUnwatchPaths.push(unwatchPath.split('\\').join('/'))
}
this.unwatchConfig = windowsUnwatchPaths
}
// setup watcher
this.watcher = chokidar.watch(this.watchPaths, this.watcherConfig)
if (this.unwatchConfig) {
this.watcher.unwatch(this.unwatchConfig)
}
// start watcher
this.watcher.on('change', this.buildOnChange.bind(this))
this.watcher.on('error', this.watchError.bind(this))
}
watchError (error) {
this.cliTools.error('WATCHER ERROR')
this.cliTools.error(error)
}
/**
* if something has changed - start building...
* @param changedPath
*/
buildOnChange (changedPath) {
this.currentChangedPath = changedPath
const displayChangedPath = this.removeBasePathFromPath(changedPath)
this.cliTools.info(`Watcher-${this.watchId}: Change ${displayChangedPath}`, !this.watcherVerbose)
// if there is a build in progress - que the change and do nothing.
if (this.isBuildInProgress()) {
this.cliTools.info(`Watcher-${this.watchId}: Change detected, but build is in Progress, will rebuild after finish`, !this.watcherVerbose)
this.rerunAfterBuild = true
return
}
// start building
this.rerunAfterBuild = false
this.buildInProgress = true
this.jobManager.reset(this.callbackKey)
this.jobManager.setCallbackOnFinish(this.watcherFinishBuildCallback.bind(this), this.callbackKey)
if (typeof(this.afterRunCallback) === 'function') {
this.beforeRunCallback.apply(this.devServer)
}
this.dispatcher.dispatchCommand(this.command)
}
/**
* watcher build is finish.
*/
watcherFinishBuildCallback () {
this.cliTools.info(`Watcher-${this.watchId}: Build finished`, !this.watcherVerbose)
this.buildInProgress = false
if (typeof(this.afterRunCallback) === 'function') {
this.afterRunCallback.apply(this.devServer)
}
if (!this.core.hasErrors() && !this.core.hasBuildNotes()) {
// Success!
this.socket.emit(this.socketCommand, this.config)
}
// if there was a change while building - rebuild this thing
if (this.rerunAfterBuild) {
this.rerunAfterBuild = false
this.cliTools.info(`Watcher-${this.watchId}: Rebuilding, cause of change while building...`, !this.watcherVerbose)
this.buildOnChange(this.currentChangedPath)
}
}
}
module.exports = FileWatcher