diff --git a/snowpack/src/commands/build.ts b/snowpack/src/commands/build.ts index 1892ba1ef4..3bb15c2d25 100644 --- a/snowpack/src/commands/build.ts +++ b/snowpack/src/commands/build.ts @@ -26,11 +26,18 @@ import { relativeURL, removeLeadingSlash, replaceExt, + HMR_CLIENT_CODE, } from '../util'; import {getInstallTargets, run as installRunner} from './install'; +import { EsmHmrEngine } from '../hmr-server-engine'; const CONCURRENT_WORKERS = require('os').cpus().length; +let hmrEngine: EsmHmrEngine | null = null; +function getIsHmrEnabled(config: SnowpackConfig) { + return config.buildOptions.watch && config.devOptions.hmr; +} + async function installOptimizedDependencies( scannedFiles: SnowpackSourceFile[], installDest: string, @@ -139,7 +146,7 @@ class FileBuilder { case '.html': { code = wrapHtmlResponse({ code, - hmr: false, + hmr: getIsHmrEnabled(this.config), isDev: false, config: this.config, mode: 'production', @@ -289,6 +296,10 @@ export async function command(commandOptions: CommandOptions) { // Write the `import.meta.env` contents file to disk await fs.writeFile(path.join(internalFilesBuildLoc, 'env.js'), generateEnvModule('production')); + if (getIsHmrEnabled(config)) { + await fs.writeFile(path.resolve(internalFilesBuildLoc, 'hmr.js'), HMR_CLIENT_CODE); + hmrEngine = new EsmHmrEngine(); + } logger.info(colors.yellow('! building sourceā€¦')); const buildStart = performance.now(); @@ -449,6 +460,10 @@ export async function command(commandOptions: CommandOptions) { await changedPipelineFile.writeProxyToDisk(builtFile); } } + + if (hmrEngine) { + hmrEngine.broadcastMessage({type: 'reload'}); + } } const watcher = chokidar.watch( mountedDirectories.map(([dirDisk]) => dirDisk), diff --git a/snowpack/src/commands/dev.ts b/snowpack/src/commands/dev.ts index f299a72884..91a6b0873a 100644 --- a/snowpack/src/commands/dev.ts +++ b/snowpack/src/commands/dev.ts @@ -29,7 +29,7 @@ import isCompressible from 'compressible'; import merge from 'deepmerge'; import etag from 'etag'; import {EventEmitter} from 'events'; -import {createReadStream, existsSync, promises as fs, readFileSync, statSync} from 'fs'; +import {createReadStream, existsSync, promises as fs, statSync} from 'fs'; import http from 'http'; import HttpProxy from 'http-proxy'; import http2 from 'http2'; @@ -76,10 +76,10 @@ import { replaceExt, resolveDependencyManifest, updateLockfileHash, + HMR_CLIENT_CODE, } from '../util'; import {command as installCommand} from './install'; import {getPort, paint, paintEvent} from './paint'; -const HMR_DEV_CODE = readFileSync(path.join(__dirname, '../assets/hmr.js')); const DEFAULT_PROXY_ERROR_HANDLER = ( err: Error, @@ -203,7 +203,8 @@ function getUrlFromFile( export async function command(commandOptions: CommandOptions) { const {cwd, config} = commandOptions; - const {port: defaultPort, hostname, open, hmr: isHmr} = config.devOptions; + const {port: defaultPort, hostname, open } = config.devOptions; + const isHmr = config.devOptions.hmr || true; // Start the startup timer! let serverStart = performance.now(); @@ -365,7 +366,7 @@ export async function command(commandOptions: CommandOptions) { }); if (reqPath === getMetaUrlPath('/hmr.js', config)) { - sendFile(req, res, HMR_DEV_CODE, reqPath, '.js'); + sendFile(req, res, HMR_CLIENT_CODE, reqPath, '.js'); return; } if (reqPath === getMetaUrlPath('/env.js', config)) { diff --git a/snowpack/src/hmr-server-engine.ts b/snowpack/src/hmr-server-engine.ts index b888750664..690e3fdf42 100644 --- a/snowpack/src/hmr-server-engine.ts +++ b/snowpack/src/hmr-server-engine.ts @@ -11,14 +11,17 @@ interface Dependency { needsReplacementCount: number; } +const DEFAULT_PORT = 12321; + export class EsmHmrEngine { clients: Set = new Set(); dependencyTree = new Map(); + wsUrl = `ws://localhost:${DEFAULT_PORT}`; constructor(options: {server?: http.Server | http2.Http2Server} = {}) { const wss = options.server ? new WebSocket.Server({noServer: true}) - : new WebSocket.Server({port: 12321}); + : new WebSocket.Server({port: DEFAULT_PORT}); if (options.server) { options.server.on('upgrade', (req, socket, head) => { // Only handle upgrades to ESM-HMR requests, ignore others. diff --git a/snowpack/src/util.ts b/snowpack/src/util.ts index f4fcd2f34f..2a4d50cac8 100644 --- a/snowpack/src/util.ts +++ b/snowpack/src/util.ts @@ -409,3 +409,5 @@ export function removeLeadingSlash(path: string) { export function removeTrailingSlash(path: string) { return path.replace(/[/\\]+$/, ''); } + +export const HMR_CLIENT_CODE = fs.readFileSync(path.join(__dirname, '../assets/hmr.js'), 'utf-8'); \ No newline at end of file