#!/usr/bin/env node // Adapted from https://github.com/hashicorp/next-remote-watch // A copy of next-remote-watch with an additional ws reload emitter. // The app listens to the event and triggers a client-side router refresh // see components/ClientReload.js const chalk = require('chalk'); const chokidar = require('chokidar'); const program = require('commander'); const http = require('http'); const SocketIO = require('socket.io'); const express = require('express'); const spawn = require('child_process').spawn; const next = require('next'); const path = require('path'); const { parse } = require('url'); const pkg = require('../package.json'); const defaultWatchEvent = 'change'; program.storeOptionsAsProperties().version(pkg.version); program .option('-r, --root [dir]', 'root directory of your nextjs app') .option( '-s, --script [path]', 'path to the script you want to trigger on a watcher event', false ) .option('-c, --command [cmd]', 'command to execute on a watcher event', false) .option( '-e, --event [name]', `name of event to watch, defaults to ${defaultWatchEvent}`, defaultWatchEvent ) .option( '-p, --polling [name]', `use polling for the watcher, defaults to false`, false ) .parse(process.argv); const shell = process.env.SHELL; const app = next({ dev: true, dir: program.root || process.cwd() }); const port = parseInt(process.env.PORT, 10) || 3000; const handle = app.getRequestHandler(); app.prepare().then(() => { // if directories are provided, watch them for changes and trigger reload if (program.args.length > 0) { chokidar .watch(program.args, { usePolling: Boolean(program.polling) }) .on( program.event, async (filePathContext, eventContext = defaultWatchEvent) => { // Emit changes via socketio io.sockets.emit('reload', filePathContext); app.server.hotReloader.send('building'); if (program.command) { // Use spawn here so that we can pipe stdio from the command without buffering spawn( shell, [ '-c', program.command .replace(/\{event\}/gi, filePathContext) .replace(/\{path\}/gi, eventContext), ], { stdio: 'inherit', } ); } if (program.script) { try { // find the path of your --script script const scriptPath = path.join( process.cwd(), program.script.toString() ); // require your --script script const executeFile = require(scriptPath); // run the exported function from your --script script executeFile(filePathContext, eventContext); } catch (e) { console.error('Remote script failed'); console.error(e); return e; } } app.server.hotReloader.send('reloadPage'); } ); } // create an express server const expressApp = express(); const server = http.createServer(expressApp); // watch files with socketIO const io = SocketIO(server); // special handling for mdx reload route const reloadRoute = express.Router(); reloadRoute.use(express.json()); reloadRoute.all('/', (req, res) => { // log message if present const msg = req.body.message; const color = req.body.color; msg && console.log(color ? chalk[color](msg) : msg); // reload the nextjs app app.server.hotReloader.send('building'); app.server.hotReloader.send('reloadPage'); res.end('Reload initiated'); }); expressApp.use('/__next_reload', reloadRoute); // handle all other routes with next.js expressApp.all('*', (req, res) => handle(req, res, parse(req.url, true))); // fire it up server.listen(port, (err) => { if (err) throw err; console.log(`> Ready on http://localhost:${port}`); }); });