server/bundler/css-hot-reload.js
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.on( '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();
		}
	} );
}
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.on( '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();
		}
	} );
}
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 . on ( '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 ( ) ; } } ) ; }
Initialize server CSS hot-reloading logic
@param {Object} io socket io socket
Initialize server CSS hot - reloading logic