UNPKG

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
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