nl_tokens: Initialize server CSS hot - reloading logic
pl_tokens: function setup ( io ) { const SCSS_PATHS = [ path . join ( '.' , 'assets' , 'stylesheets' ) , path . join ( '.' , 'client' ) ] ; const ROOT_DIR = path . resolve ( __dirname , '..' , '..' ) ; const PUBLIC_DIR = path . join ( ROOT_DIR , 'public' ) ; let cssMake = null , errors = '' , scheduleBuild = false ; const publicCssFiles = { } ; // { path, hash } dictionary function spawnMake ( ) { // If a build is already in progress schedule another build for later if ( cssMake ) { scheduleBuild = true ; return ; } io . of ( '/css-hot-reload' ) . emit ( 'css-hot-reload' , { status : 'building' } ) ; debug ( 'spawning %o' , 'npm run build-css' ) ; cssMake = spawn ( 'npm' , [ 'run' , 'build-css' ] , { shell : true , cwd : ROOT_DIR , stdio : [ 'ignore' , 'pipe' , 'pipe' ] , } ) ; errors = '' ; cssMake . once ( 'exit' , onexit ) ; cssMake . stdout . setEncoding ( 'utf8' ) ; cssMake . stderr . setEncoding ( 'utf8' ) ; cssMake . stdout . <mask> ( 'data' , onstdout ) ; cssMake . stderr . on ( 'data' , onstderr ) ; } function onstdout ( d ) { debug ( 'stdout %o' , d . trim ( ) ) ; } function onstderr ( stderr ) { process . stderr . write ( stderr ) ; // Note: // Because we use make --jobs the error message is usually duplicated. errors += stderr ; } /**
  	 * Handle 'make build-css' success or failure
  	 * @param {Number} code The exit code
  	 */ function onexit ( code ) { let changedFiles ; cssMake . stderr . removeListener ( 'data' , onstderr ) ; cssMake . stdout . removeListener ( 'data' , onstdout ) ; cssMake = null ; if ( scheduleBuild ) { // Handle new css build request scheduleBuild = false ; spawnMake ( ) ; } else if ( 0 === code ) { // 'make build-css' success changedFiles = updateChangedCssFiles ( ) ; if ( 0 !== changedFiles . length ) { debug ( chalk . green ( 'css reload' ) ) ; io . of ( '/css-hot-reload' ) . emit ( 'css-hot-reload' , { status : 'reload' , changedFiles : changedFiles , } ) ; } else { debug ( chalk . green ( 'css up to date' ) ) ; io . of ( '/css-hot-reload' ) . emit ( 'css-hot-reload' , { status : 'up-to-date' } ) ; } } else { // 'make build-css' failed debug ( chalk . red ( 'css build failed' ) ) ; io . of ( '/css-hot-reload' ) . emit ( 'css-hot-reload' , { status : 'build-failed' , error : errors , } ) ; } } /**
  	 * Updates the MD5 hash of the public CSS files and returns the changed ones
  	 * in a list.
  	 * @returns {Array} an array of changed files (string)
  	 */ function updateChangedCssFiles ( ) { let hash , filePath ; const changedFiles = [ ] ; for ( filePath in publicCssFiles ) { hash = md5File . sync ( filePath ) ; if ( hash !== publicCssFiles [ filePath ] ) { publicCssFiles [ filePath ] = hash ; changedFiles . push ( path . basename ( filePath ) ) ; } } return changedFiles ; } // Initialize publicCssFiles fs . readdirSync ( PUBLIC_DIR ) . forEach ( function ( file ) { if ( '.css' === file . slice ( - 4 ) ) { const fullPath = path . join ( PUBLIC_DIR , file ) ; publicCssFiles [ fullPath ] = md5File . sync ( fullPath ) ; } } ) ; // Watch .scss files const watcher = chokidar . watch ( SCSS_PATHS , { ignored : / ^.*\.(js[x]?|md|json|unison\.tmp)$ / , usePolling : false , persistent : true , } ) ; // The add/addDir events are fired during initialization generating an // avalanche of refreshes on the client so we stick to the change events // only for now until we can exclude the initial add/addDir events. watcher . on ( 'all' , function ( event , filepath ) { if ( 'change' === event && filepath . match ( / ^.*\.scss$ / ) ) { spawnMake ( ) ; } } ) ; }